Julia way of searching tokens in integer arrays - julia

Let's say I have buffer=Int[1,2,3,2,3] and token=[2,3].
Is there any preferred way of searching the occurrence of token in buffer to find [2,4] as the answer.
Or, perhaps, is there any split equivalent function for the integer arrays in julia?
(I know how I can perform this operation using 2 nested loops. However, I am especially interested if there is a more Julian way of doing this.)

Because Julia doesn't have conditionals in list comprehensions, I would personally use filter(). Thus if arr = Int64[1,2,3,4,5,2,3,6,2,3,3,2,2]:
filter(x -> arr[x] == 2 && arr[x + 1] == 3, 1 : length(arr) - 1)
=> [2,6,9]
To make it a little more reusable:
pat = [2,3]
filter(x -> arr[x : x + length(pat) - 1] == pat, 1 : length(arr) - length(pat) + 1)
=> [2,6,9]
Julia does have built-ins like find([fun], A), but there's no way that I'm aware of to use them to return indexes of an ordered sublist.
Of course it's arguably more legible to just
ndxs = Int64[]
for i = 1:length(arr)-1
if arr[i] == 2 && arr[i+1] == 3
push!(ndxs, i)
end
end
=> [2,6,9]

For practice I have also made trial-and-errors and the following patterns have worked for Julia0.4.0. With A = Int[1,2,3,2,3] and pat = Int[2,3], the first one is
x = Int[ A[i:i+1] == pat ? i : 0 for i=1:length(A)-1 ]
x[ x .> 0 ] # => [2,4]
the second one is
x = Int[]
[ A[i:i+1] == pat ? push!(x,i) : 0 for i=1:length(A)-1 ]
#show x # => [2,4]
and the third one is
find( [ A[i:i+1] == pat for i=1:length(A)-1 ] ) # => [2,4]
(where find() returns the index array of true elements). But personally, I feel these patterns are more like python than julia way...

Related

Check multiple conditions on the same function output in Julia

Is there a way to check multiple Boolean conditions against a value (to achieve the same as below) without computing sum twice or saving the result to a variable?
if sum(x) == 1 || sum(x) > 3
# Do Something
end
You can use one of several options:
Anonymous function:
if (i->i>3||i==1)(sum(x))
# Do Something
end
Or,
if sum(x) |> i->i>3||i==1
# Do Something
end
DeMorgan's theorem:
if !(3 >= sum(x) != 1)
# Do Something
end
And if used inside a loop:
3 >= sum(x) != 1 && break
# Do Something
or as function return:
3 >= sum(x) != 1 && return false
But using a temporary variable would be the most readable of all:
s = sum(x)
if s > 3 || s == 1
# Do Something
end
Syntactically, a let is valid in that position, and the closest equivalent to AboAmmar's variant with a lambda:
if let s = sum(x)
s == 1 || s > 3
end
# do something
end
I'd consider this rather unidiomatic, though.

sum function in Julia is giving error if the array is empty

I am trying to create a code which identifies if the elements in an array are monotonic or not.
I wrote the below code and got the error -
function isMonotonic(array)
if length(array) <= 2
return true
end
check_up = []
check_down = []
for i in range(2, length(array))
if array[i] <= array[i-1]
append!(check_up, 1)
end
if array[i] >= array[i - 1]
append!(check_down, 1)
end
end
if sum(check_up) == length(array) - 1 || sum(check_down) == length(array) - 1
return true
else
return false
end
end
isMonotonic([1, 2, 3, 4, 5, 6 , 7])
I am getting the below error
Error: Methoderror: no method matching zero(::Type{Any})
I think it is because I am trying to sum up the empth array, I want to understand how to overcome this problem in general, I have a solution for the above code, but in genral I want to know the reason and how to use it. I do not want to first check if the array is empty or not and then do the sum.
If you wanted to save yourself lots of effort, the simplest solution would just be:
my_ismonotonic(x) = issorted(x) || issorted(x ; rev=true)
This will return true if x is sorted either forwards, or in reverse, and false otherwise.
We could maybe make it a little more efficient using a check so we only need a single call to issorted.
function my_ismonotonic(x)
length(x) <= 2 && return true
for n = 2:length(x)
if x[n] > x[1]
return issorted(x)
elseif x[n] < x[1]
return issorted(x ; rev=true)
end
end
return true
end
# Alternatively, a neater version using findfirst
function my_ismonotonic(x)
length(x) <= 2 && return true
ii = findfirst(a -> a != x[1], x)
isnothing(ii) && return true # All elements in x are equal
if x[ii] > x[1]
return issorted(x)
else
return issorted(x ; rev=true)
end
end
The loop detects the first occurrence of an element greater than or less than the first element and then calls the appropriate issorted as soon as this occurs. If all elements in the array are equal then the loop runs over the whole array and returns true.
There are a few problems of efficiency in your approach, but the reason you are getting an actual error message is because given the input, either this expression sum(check_up) or this expression sum(check_down) will effectively result in the following call:
sum(Any[])
There is no obvious return value for this since the array could have any type, so instead you get an error. If you had used the following earlier in your function:
check_up = Int[]
check_down = Int[]
then you shouldn't have the same problem, because:
julia> sum(Int[])
0
Note also that append! is usually for appending a vector to a vector. If you just want to add a single element to a vector use push!.

Map, reduce, filter apply to for loops and while loops

I'm new to Julia and learning use of Map, reduce, filter.
It is becoming very hard for me to comprehend how it can replace for and while loops.
For ex for below code, I would like to replace for loop
function addMultiplesOf3And5(N::Int)
sumOfMultiples = 0
if(N == 3)
return sumOfMultiples + N
end
for i = 3:N-1
if(i % 3 == 0 && i % 5 == 0)
continue
elseif(i % 3 == 0)
sumOfMultiples += i
elseif(i % 5 == 0)
sumOfMultiples += i
end
end
return sumOfMultiples
end
I would really appreciate the help.
Update :
This is what I did after going through tutorials
function addMultiplesOf3And5(N::Int)
array = range(1,N-1)
return reduce(+, map(x -> multiples_of_3_Or_5(x), array))
end
function multiples_of_3_Or_5(n)
if(n % 3 == 0 && n % 5 == 0)
return 0
elseif(n % 3 == 0)
return n
elseif(n % 5 == 0)
return n
else
return 0
end
end
Final:
function addMultiplesOf3And5(N::Int)
array = range(1,N-1)
return reduce(+, filter(x -> ((x%3==0)$(x%5==0)), array))
end
To understand how you can replace your 'for loop + if block' code with 'map / reduce / filter' you need to know exactly how they work and why they might be chosen instead.
1. The map function
map is a function that takes a function variable and a list as arguments, and returns a new list, where each element is the result of applying the function to each element of the old list. So for example if your variable f refers to a function f(x) = x + 5 you defined earlier, and you have a list L=[1,2,3,4,5], then map(f, L) will return [f(L[1]), f(L[2]), f(L[3]), f(L[4]), f(L[5])]
So if you have code like:
f(x) = x + 5;
L = [1,2,3,4,5];
A = zeros(5);
for i in L
A[i] = f(i);
end
You could rewrite this as a mapping operation like so:
A = map(x -> x + 5, [1,2,3,4,5]);
2. The reduce function
reduce takes a binary function variable (i.e. a function that takes two arguments) and a list as arguments. What it does is probably best explained by an example. Calling reduce with the + operator, and list [1,2,3,4,5] will do the following:
Step 1: [1, 2, 3, 4, 5] % : 5 elements
Step 2: [1+2, 3, 4, 5] % [3,3,4,5] : 4 elements
Step 3: [3+3, 4, 5] % [6, 4, 5] : 3 elements
Step 4: [6+4, 5] % [10, 5] : 2 elements
Step 5: [10+5] % [15] : 1 elements
result: 15
i.e. we have reduced the list to a single result by successively applying the binary function to the first pair of elements, consuming the list little by little.
So if you have code like:
f(x,y) = x + y
L = [1,2,3,4,5];
A = L[1];
for i in 2:length(L)
A = f(A, L[i])
end
you could rewrite this as a reduce operation like so:
A = reduce(x,y -> x+y, [1,2,3,4,5])
3. The filter function
filter takes a predicate function (e.g. iseven, isnull, ==, or anything that takes an argument and performs a test on it, resulting in true or false) and a list, tests each element of the list with the function and returns a new list that only contains the elements that pass that test. e.g.
filter(iseven, [1,2,3,4,5]) # returns [2,4]
The answer to your problem
If I understand correctly, addMultiplesOf3And5 takes a number N (e.g. 20), and does the following:
filter out all the elements that can be divided by either 3 or 5 from the list [1,2,3,...,20]
successively add all elements of the resulting list together using a reduce function.
You should be able to use the above to figure out the exact code :)
Not sure what the function in the question is supposed to calculate, but:
addMult3or5(N) = N==3 ? 3 : sum(filter(x->((x%3==0)$(x%5==0)),3:N-1))
calculates the same thing.
sum is a a reduce-like function for the + operation.
Hope this helps clarify.
Also, $ is the exclusive-or operation in Julia.

How to stop recursing?

Advent of Code Day 1 requires looping, in one form or another, over a long string of parentheses like ((((())(())(((()))(( etc. The idea is that ( goes up one "floor", ) goes down one floor, and the objectives are to print
the first index in the string where the floor number is negative and
the final floor when the end of the string is found.
The imperative solution with a for loop is simple (Python as an example):
def main():
flr = 0
basement = False
for idx, elt in enumerate(text):
flr += {
"(": 1,
")": -1
}.get(elt)
if flr < 0 and not basement:
print("first basement pos:", idx + 1)
basement = True
print("final floor:", flr)
The recursive functional solution is a little more complex, but still not too hard.
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return flr
if flr < 0 and not basement:
print("first basement floor index: ", idx + 1)
basement = True
return worker(flr, txt[1:], idx + 1, basement)
def starter(txt):
flr, basement, idx = 0, False, 0
return worker(flr, txt, idx, basement)
if __name__ == '__main__':
__import__("sys").setrecursionlimit(int(1e5))
print("final floor:", starter(text))
Both of these give the correct output of
first basement floor index: 1795
final floor: 74
when run against my challenge input.
except the second one is dumb because Python doesn't have tail call optimisation but never mind that
How can I implement either of these in Factor? This is something I've been confused by ever since I started using Factor.
We can't just use a for loop because there's no equivalent that allows us to keep mutable state between iterations.
We could use a recursive solution:
: day-1-starter ( string -- final-floor )
[ 0 ] dip 0 f day-1-worker 3drop "final floor: %s" printf ;
: day-1-worker
( floor string index basement? -- floor string index basement? )
day-1-worker ! what goes here?
; recursive
Great, that's a skeleton, but what goes in the body of day-1-worker? Factor doesn't have any way to "early return" from a recursive call because there's no way to run the program in reverse and no concept of return -- that doesn't make any sense.
I get the feeling maybe recursion isn't the answer to this question in Factor. If it is, how do I stop recursing?
First of all, recursion is always the answer :)
Since this is a challenge (and I don't know factor), just a hint:
in your python solution you have used the side effect to print the first basement level. Quite unnecessary! You can use basemet argument to hold the floor number too, like this:
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return [flr, basement] # <- return both
if flr < 0 and not basement:
#print("first basement floor index: ", idx + 1) # side effects go away!
basement = idx+1 # <- a number in not False, so that's all
return worker(flr, txt[1:], idx + 1, basement)
So now you get
final,first_basement = worker(0, txt, 0, False)
Or, alternatively you can write 2 functions, first one seeks the index of first basement floor, the other one just computes the final floor. Having <2000 additional small steps is not a big deal even if you do care about performance.
Good luck!
Edit: as of your question concerning recursion in factor, take a look at the Ackermann Function in Factor and the Fibonacci sequence in Factor and you should get the idea how to "break the loop". Actually the only problem is in thinking (emancipate yourself from the imperative model :)); in functional languages there is no "return", just the final value, and stack-based languages you mention are other computational model of the same thing (instead of thinking of folding a tree one thinks about "pushing and poping to/from the stacks" -- which is btw a common way to implement the former).
Edit: (SPOILER!)
I installed Factor and started playing with it (quite nice), for the first question (computing the final score) a possible solution is
: day-1-worker ( string floor -- floor )
dup length 0 =
[ drop ]
[ dup first 40 =
[ swap 1 + ]
[ swap 1 - ]
if
swap rest
day-1-worker ]
if ;
: day-1-starter ( string -- floor )
0 swap day-1-worker ;
So now you can either write similar one for computing basement's index, or (which would be more cool!) to modify it so that it also manages index and basement... (Probably using cond would be wiser than nesting ifs).
You could use the cum-sum combinator:
: to-ups/downs ( str -- seq )
[ CHAR: ( = 1 -1 ? ] { } map-as ;
: run-elevator ( str -- first-basement final-floor )
to-ups/downs cum-sum [ -1 swap index 1 + ] [ last ] bi ;
IN: scratchpad "((())))(())(())(((()))((" run-elevator
--- Data stack:
7
2
EDIT
I originally misread how your were computing the basement value. I've updated the answers below
Here's a JavaScript solution. Sorry I have no idea how this converts to Factor. reduce is an iterative process
const worker = txt=>
txt.split('').reduce(({floor, basement}, x, i)=> {
if (x === '(')
return {floor: floor + 1, basement}
else if (basement === null && floor === 0)
return {floor: floor - 1, basement: i}
else
return {floor: floor - 1, basement}
}, {floor: 0, basement: null})
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
The answer above relies on some some .split and .reduce which may not be present in your language. Here's another solution using Y-combinator and only the substring built-in (which most languages include). This answer also depends on your language having first-class functions.
const U = f=> f (f)
const Y = U (h=> f=> f (x=> h (h) (f) (x)))
const strhead = s=> s.substring(0,1)
const strtail = s=> s.substring(1)
const worker = Y (f=> ({floor, basement})=> i=> txt=> {
// txt is empty string; return answer
if (txt === '')
return {floor, basement}
// first char in txt is '(', increment the floor
else if (strhead (txt) === '(')
return f ({floor: floor + 1, basement}) (i+1) (strtail (txt))
// if basement isn't set and we're on floor 0, we found the basement
else if (basement === null && floor === 0)
return f ({floor: floor - 1, basement: i}) (i+1) (strtail (txt))
// we're already in the basement, go down another floor
else
return f ({floor: floor - 1, basement}) (i+1) (strtail (txt))
}) ({floor: 0, basement: null}) (0)
{
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
}
{
let {floor, basement} = worker(')(((((')
console.log(floor) //=> 4
console.log(basement) //=> 0
}
{
let {floor, basement} = worker('((())))')
console.log(floor) //=> -1
console.log(basement) //=> 6
}

need help understanding this erlang code

I'm having trouble understanding this line.
[Pid2 ! {delete, V1a}
|| {Pid1a, V1a} <- PV1a, Pid2 <- P2, Pid1a /= Pid2
],
Here is what I understand:
anything before the double pipe "||" is done repeatedly, according what's after the double pipe. so messages with delete atom is repeated sent to Pid2.
I know '/=' mean inequality. I don't understand what '<-' means, and ultimately what the whole line means.
[something(X) || X <- L], is a list comprehension. L is a list of elements, and this expression creates a list of new elements, forming each element by invoking something() on it.
[something(X,Y) || X <-L, Y<-M] is similar, but an element is created for the Cartesian product of each element in X and Y.
[something(X) || X <-L, Expr] is a filter expression. Same as the first one, but it is executed only for elements of L, where Expr is true for the given X.
[something(X) || {X,..} <-L, Expr] is another kind of filter. In the list comprehension only those elements are taken that can be matched by the element.
One more thing to know is that this can not only be used for generating another list, but also for executing a command for each element. If the result of the list comprehension is not matched, the compiler will know not to generate a list at all. This behavior can be used to mimic foreach from other languages.
Some examples:
1> [ X*2 || X <- [1,2,3] ].
[2,4,6]
2> [ X*Y || X <- [1,2], Y <- [3,4,5] ].
[3,4,5,6,8,10]
3> [ X*3 || X <- [1,2,3,4], X rem 2 == 0 ].
[6,12]
4> [ X || {a,X} <- [{a,1},{a,2},{b,3},{c,4}] ].
[1,2]
So your code generates the Cartesian product of all {Pid1a, V1a} elements from PV1a and Pid2 elements from P2, except for those elements where Pid1a equals Pid2, and for each of these pairs sends the {delete, V1a} message to Pid2.
I don't know Erlang, but this looks just like list comprehensions from a bunch of languages I do know. Hopefully this guess will help you until somebody who knows Erlang can answer:
[Pid2 ! {delete, V1a} || {Pid1a, V1a} <- PV1a, Pid2 <- P2, Pid1a /= Pid2],
Translates to imperative-style pseudocode:
For each item in PV1a, unpacking item to {Pid1a, V1a}
For each Pid2 in P2
If Pid1a /= Pid2
Pid2 ! {delete, V1a}
In other words, for each Pid in PV1a and P2, send the message delete V1a to Pid2 as long as Pid1 and Pid2 are not the same Pid.
It is a list comprehension and the <- operator is used for generators.
Look at a more popular introduction example for LCs; to find triangles where the squares of the integer sides equals the square of the integer hypotenuse, but for a given range of integers Ns.
Ns = [1,2,3,4,5,6,7,8,9].
[{X,Y,C} || X <- Ns, Y <- Ns, C <- Ns, X*X + Y*Y == C*C].
This gives us the following list as output.
[{3,4,5},{4,3,5}]
Which seems correct:
3² + 4² = 5²
9 + 16 = 25
25 = 25
So the list comprehension can be read as give us every X,Y and C such that X is taken from Ns, Y is taken from Ns and C is taken from Ns, and X² + Y² equals C².

Resources