Oh, sorry, that’s what you meant, yeah that get’s closed as well. As you said the do is really syntactic sugar for passing in a function.
broadcast(A, B) do a, b
q = a + b
c = 3q - 4
c / q
end
I’d like it even better if there was something like the Haskell where
construct (not just for do notation), along the lines of
begin
c / q
where
q = a + b
c = 3q - 4
end
in the spirit of “show me the big picture, then the details”.
The following are equivalent (inside a function body):
-
An anonymous closure
map((x, y) -> ...body..., xs, ys)
-
a
do
blockmap(xs, ys) do x, y ...body... end
-
a function that you name
function _f(x, y) ...body... end map(_f, xs, ys)
While I like the do
block syntax, I frequently use the third option too, especially because if _f
is complicated, I can just return it from the containing function for further debugging (as a closure).
I saw today for the first time (I’m a beginner) the use of the map () do … end block.
Trying to understand how it works, I did some tests.
The example I saw had this structure roughly:
itr=zip(1:12,'a':'l')
map(itr) do (n,c)
c^n
end
I tried to rewrite it like this(which should be the translation in the in-line form)::
map((n,c)->c^n,itr)
but the result is different. indeed it does not seem to be a correct syntax.
to get the same result(*) I had to use two different syntax:
-
map(c->string(c[2]^c[1]),itr)
-
map((n,c)->c^n, collect(1:12),collect('a':'l'))
therefore the two forms are not always "directly and simply " exchangeable?!?!?
(*)
12-element Array{String,1}:
"a"
"bb"
"ccc"
"dddd"
"eeeee"
⋮
"hhhhhhhh"
"iiiiiiiii"
"jjjjjjjjjj"
"kkkkkkkkkkk"
"llllllllllll"
You want map(((n,c),)->c^n,itr)
to destructure the tuples obtained by iterating over a zip
.
I like Python’s version much better. It has an as
keyword which is more intuitive to me.
It’s interesting to see how much preferences can vary here. I really enjoy the Julia/Ruby/Kotlin/Scala/Elixir model of exposing a lambda as a block instead of having dedicated context managers. Python context managers are inflexible and require a lot of boilerplate for custom implementations. with
blocks also don’t create new scope and thus pollute the current scope with more names that you can’t use for other variables.
Then this means that the do … end block does this destructuring operation automatically (whatever that means )?
It’s not specific to the do
block. Maybe it’s clearer when looking at a function that has both normal and destructured arguments:
function f((a,b), c) # Function of 2 arguments
a+b+c
end
tup = (1,2)
f(tup, 3) # result: 6
An anonymous function doing the same would be ((a,b), c) -> a+b+c
.
With a do
block it’s the same. Instead of map(f, [tup, tup, tup], [3, 4, 5])
you can write
map([tup, tup, tup], [3, 4, 5]) do (a,b), c
a+b+c
end
The only difference is that the do
arguments are not written in parentheses, so do a, b
declares a function of two arguments while do (a, b)
declares a function of one argument, which is then destructed in two variables a
and b
. This is admittedly not obvious when looking at the do (a, b)
syntax in isolation
Since the thread has been revived, here’s an example where I find the do
syntax particularly intuitive:
julia> using Makie
julia> scene = Scene()
julia> on(scene.events.mouseposition) do pos
println("Mouse is at $pos")
end
I see the point.
tanks.
I ran into a situation where I tried to brodcast on a vector of tuples and ran into difficulties, which I got over.
But I would like to understand better, how things are going.
I wanted to check if a given tuple was contained in a list of tuples and I used a similar to this expression which gave me error:
julia> (2,3) .==[(2,3),(1,1),(5,4)]
ERROR: DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths 2 and 3")
I solved it by changing it like this:
((2,3),) .==[(2,3),(1,1),(5,4)]
I would like to understand how it works instead in the following case:
julia> (2,) .== [(2,),3,4,2,5]
5-element BitArray{1}:
0
0
0
1
0
That’s just how broadcasting works - the tuple (2,3)
on the left hand side is iterable, so broadcasting will try to iterate over its contents. You are trying to do
[2 == (2,3), 3 == (1,1), ⋆ == (5,4)]
where ⋆
here denotes the missing element on the left hand side that the error complains about (2 elements in the tuple on the LHS, 3 elements in your vector of tuples on the RHS).
Wrapping the LHS in a tuple is one way of doing this, another would be to use Ref
to make clear that the LHS is to be treated as a scalar:
julia> Ref((2, 3)) .== [(2,3), (1,1), (5,4)]
3-element BitArray{1}:
1
0
0
i tried this but without success:smile:
julia> do (2,3) end .==[(2,3),(1,1),(5,4)]
ERROR: syntax: invalid "do" syntax
What are you trying to do here?
An equivalent approach to the broadcast would be:
map([(2,3),(1,1),(5,4)]) do elem
elem == (2, 3)
end
The do
syntax is to be used after some method call that takes a function
/closure/callable as first parameter. You ommit the first parameter (the callable) and instead define you a closure with the parameters after the do
(in the same line) and the body of the closure in the following lines until the end
.
Ok guys this confused the hell out of me until I saw the official do keyword documentation: Essentials · The Julia Language
Basically, here’s what do does:
map(1:10, 11:20) do x, y
x + y
end
is the same as:
map((x,y)->(x+y), 1:10, 11:20)
So anywhere that a function takes another function as a first argument, it is valid to use a do block to define what that first argument function is.