Vectorizing groups of function calls
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I am trying to write a functionality, (using macro, generated function or something), that effectively vectorizes Julia function calls to functions that I've written. Basically, I'm trying to write my own version of the @. macro, but instead, I'd like it to accept functions instead of a for loop--- if I understand this correctly. Here are some documents that I've read on the subject:
https://docs.julialang.org/en/v1/manual/functions/#man-vectorized-1
https://github.com/JuliaLang/julia/blob/master/base/broadcast.jl
https://julialang.org/blog/2017/01/moredots
https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#Code-Generation-1
Here is my preliminary toy example that I'm working with to achieve such a functionality:
function add!(v_add::VectorFloat64, a_add::Float64, j::Int64)
v_add[j] = v_add[j]+a_add
end
function add!(v_add::VectorFloat64, a_add::Float64)
for j in 1:length(v_add)
v_add[j] = v_add[j]+a_add
end
end
macro vectorize(args)
print("n****************** argsn")
print(args)
print("n******************n")
e = :(:call,
$args[1],
$args[2],
$args[3])
print("n****************** expressionn")
show(e)
print(e)
print("n******************n")
return e
end
function test!(v_test, a_test)
# # Traverse vector twice
# add!(v_test, a_test)
# add!(v_test, a_test)
# Traverse vector once
args = [
add!, v_test, a_test,
add!, v_test, a_test
]
e = @vectorize(args)
# eval(e) # Next step
end
v_main = Vector([Float64(i) for i in 1:3])
a_main = Float64(2.0)
print("n",v_main, "n")
Main.test!(v_main, a_main)
print("n",v_main, "n")
The problem I'm having so far is that I can't even get the de-vectorized version running using macros. This example results in the LoadError: UndefVarError: args not defined. I would definitely appreciate any help in getting this script working as expected (input is [1, 2, 3], and output should be [5, 6, 7]).
Any help/suggestions are greatly appreciated.
Update
More concretely, given the following defined functions:
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
I would like to be able to use a macro to convert the following lines of code:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
@vectorize_I_would_like_to_define(
# I don't know the exact form that the args to this macro should take.
add!(v, a),
add!(v, b)
)
To generate code that is compiled like this:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
add!(v, a, j)
add!(v, b, j)
end
My goal is to write code that requires a single memory traversal.
Even better, if I could generate code that looks like this at compile time:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
v[j]+= a # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= b # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
end
But I'm not sure if this is as feasable for more complex cases that I'm considering compared to this toy example.
** Update 2**
Here is a MWE of @Bogumił Kamiński's solution---except that I've moved the macro call into a function, so now it doesn't work because it complains that v_test
is not defined.
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
function test!(v_test, a_test, b_test)
@vectorize(
add!(v_test, a_test),
add!(v_test, b_test)
)
end
test!(v, a, b)
function macros julia vectorization metaprogramming
add a comment |
I am trying to write a functionality, (using macro, generated function or something), that effectively vectorizes Julia function calls to functions that I've written. Basically, I'm trying to write my own version of the @. macro, but instead, I'd like it to accept functions instead of a for loop--- if I understand this correctly. Here are some documents that I've read on the subject:
https://docs.julialang.org/en/v1/manual/functions/#man-vectorized-1
https://github.com/JuliaLang/julia/blob/master/base/broadcast.jl
https://julialang.org/blog/2017/01/moredots
https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#Code-Generation-1
Here is my preliminary toy example that I'm working with to achieve such a functionality:
function add!(v_add::VectorFloat64, a_add::Float64, j::Int64)
v_add[j] = v_add[j]+a_add
end
function add!(v_add::VectorFloat64, a_add::Float64)
for j in 1:length(v_add)
v_add[j] = v_add[j]+a_add
end
end
macro vectorize(args)
print("n****************** argsn")
print(args)
print("n******************n")
e = :(:call,
$args[1],
$args[2],
$args[3])
print("n****************** expressionn")
show(e)
print(e)
print("n******************n")
return e
end
function test!(v_test, a_test)
# # Traverse vector twice
# add!(v_test, a_test)
# add!(v_test, a_test)
# Traverse vector once
args = [
add!, v_test, a_test,
add!, v_test, a_test
]
e = @vectorize(args)
# eval(e) # Next step
end
v_main = Vector([Float64(i) for i in 1:3])
a_main = Float64(2.0)
print("n",v_main, "n")
Main.test!(v_main, a_main)
print("n",v_main, "n")
The problem I'm having so far is that I can't even get the de-vectorized version running using macros. This example results in the LoadError: UndefVarError: args not defined. I would definitely appreciate any help in getting this script working as expected (input is [1, 2, 3], and output should be [5, 6, 7]).
Any help/suggestions are greatly appreciated.
Update
More concretely, given the following defined functions:
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
I would like to be able to use a macro to convert the following lines of code:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
@vectorize_I_would_like_to_define(
# I don't know the exact form that the args to this macro should take.
add!(v, a),
add!(v, b)
)
To generate code that is compiled like this:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
add!(v, a, j)
add!(v, b, j)
end
My goal is to write code that requires a single memory traversal.
Even better, if I could generate code that looks like this at compile time:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
v[j]+= a # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= b # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
end
But I'm not sure if this is as feasable for more complex cases that I'm considering compared to this toy example.
** Update 2**
Here is a MWE of @Bogumił Kamiński's solution---except that I've moved the macro call into a function, so now it doesn't work because it complains that v_test
is not defined.
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
function test!(v_test, a_test, b_test)
@vectorize(
add!(v_test, a_test),
add!(v_test, b_test)
)
end
test!(v, a, b)
function macros julia vectorization metaprogramming
I am not 100% clear what you want to achieve. Could you please give a description of an input and expected processing workflow?
– Bogumił Kamiński
Nov 15 '18 at 6:56
@BogumiłKamiński, I added a more concrete example of the functionality that I'm looking to achieve.
– Charles
Nov 15 '18 at 16:04
EDIT and EDIT 2 in my answer work with your example (I leave both of them as I think it is helpful to analyze the difference between them).
– Bogumił Kamiński
Nov 15 '18 at 22:09
add a comment |
I am trying to write a functionality, (using macro, generated function or something), that effectively vectorizes Julia function calls to functions that I've written. Basically, I'm trying to write my own version of the @. macro, but instead, I'd like it to accept functions instead of a for loop--- if I understand this correctly. Here are some documents that I've read on the subject:
https://docs.julialang.org/en/v1/manual/functions/#man-vectorized-1
https://github.com/JuliaLang/julia/blob/master/base/broadcast.jl
https://julialang.org/blog/2017/01/moredots
https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#Code-Generation-1
Here is my preliminary toy example that I'm working with to achieve such a functionality:
function add!(v_add::VectorFloat64, a_add::Float64, j::Int64)
v_add[j] = v_add[j]+a_add
end
function add!(v_add::VectorFloat64, a_add::Float64)
for j in 1:length(v_add)
v_add[j] = v_add[j]+a_add
end
end
macro vectorize(args)
print("n****************** argsn")
print(args)
print("n******************n")
e = :(:call,
$args[1],
$args[2],
$args[3])
print("n****************** expressionn")
show(e)
print(e)
print("n******************n")
return e
end
function test!(v_test, a_test)
# # Traverse vector twice
# add!(v_test, a_test)
# add!(v_test, a_test)
# Traverse vector once
args = [
add!, v_test, a_test,
add!, v_test, a_test
]
e = @vectorize(args)
# eval(e) # Next step
end
v_main = Vector([Float64(i) for i in 1:3])
a_main = Float64(2.0)
print("n",v_main, "n")
Main.test!(v_main, a_main)
print("n",v_main, "n")
The problem I'm having so far is that I can't even get the de-vectorized version running using macros. This example results in the LoadError: UndefVarError: args not defined. I would definitely appreciate any help in getting this script working as expected (input is [1, 2, 3], and output should be [5, 6, 7]).
Any help/suggestions are greatly appreciated.
Update
More concretely, given the following defined functions:
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
I would like to be able to use a macro to convert the following lines of code:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
@vectorize_I_would_like_to_define(
# I don't know the exact form that the args to this macro should take.
add!(v, a),
add!(v, b)
)
To generate code that is compiled like this:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
add!(v, a, j)
add!(v, b, j)
end
My goal is to write code that requires a single memory traversal.
Even better, if I could generate code that looks like this at compile time:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
v[j]+= a # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= b # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
end
But I'm not sure if this is as feasable for more complex cases that I'm considering compared to this toy example.
** Update 2**
Here is a MWE of @Bogumił Kamiński's solution---except that I've moved the macro call into a function, so now it doesn't work because it complains that v_test
is not defined.
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
function test!(v_test, a_test, b_test)
@vectorize(
add!(v_test, a_test),
add!(v_test, b_test)
)
end
test!(v, a, b)
function macros julia vectorization metaprogramming
I am trying to write a functionality, (using macro, generated function or something), that effectively vectorizes Julia function calls to functions that I've written. Basically, I'm trying to write my own version of the @. macro, but instead, I'd like it to accept functions instead of a for loop--- if I understand this correctly. Here are some documents that I've read on the subject:
https://docs.julialang.org/en/v1/manual/functions/#man-vectorized-1
https://github.com/JuliaLang/julia/blob/master/base/broadcast.jl
https://julialang.org/blog/2017/01/moredots
https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#Code-Generation-1
Here is my preliminary toy example that I'm working with to achieve such a functionality:
function add!(v_add::VectorFloat64, a_add::Float64, j::Int64)
v_add[j] = v_add[j]+a_add
end
function add!(v_add::VectorFloat64, a_add::Float64)
for j in 1:length(v_add)
v_add[j] = v_add[j]+a_add
end
end
macro vectorize(args)
print("n****************** argsn")
print(args)
print("n******************n")
e = :(:call,
$args[1],
$args[2],
$args[3])
print("n****************** expressionn")
show(e)
print(e)
print("n******************n")
return e
end
function test!(v_test, a_test)
# # Traverse vector twice
# add!(v_test, a_test)
# add!(v_test, a_test)
# Traverse vector once
args = [
add!, v_test, a_test,
add!, v_test, a_test
]
e = @vectorize(args)
# eval(e) # Next step
end
v_main = Vector([Float64(i) for i in 1:3])
a_main = Float64(2.0)
print("n",v_main, "n")
Main.test!(v_main, a_main)
print("n",v_main, "n")
The problem I'm having so far is that I can't even get the de-vectorized version running using macros. This example results in the LoadError: UndefVarError: args not defined. I would definitely appreciate any help in getting this script working as expected (input is [1, 2, 3], and output should be [5, 6, 7]).
Any help/suggestions are greatly appreciated.
Update
More concretely, given the following defined functions:
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
I would like to be able to use a macro to convert the following lines of code:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
@vectorize_I_would_like_to_define(
# I don't know the exact form that the args to this macro should take.
add!(v, a),
add!(v, b)
)
To generate code that is compiled like this:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
add!(v, a, j)
add!(v, b, j)
end
My goal is to write code that requires a single memory traversal.
Even better, if I could generate code that looks like this at compile time:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
v[j]+= a # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= b # taken from add!(v::VectorFloat64, a::Float64, j::Int64)
end
But I'm not sure if this is as feasable for more complex cases that I'm considering compared to this toy example.
** Update 2**
Here is a MWE of @Bogumił Kamiński's solution---except that I've moved the macro call into a function, so now it doesn't work because it complains that v_test
is not defined.
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
function test!(v_test, a_test, b_test)
@vectorize(
add!(v_test, a_test),
add!(v_test, b_test)
)
end
test!(v, a, b)
function macros julia vectorization metaprogramming
function macros julia vectorization metaprogramming
edited Nov 15 '18 at 20:15
Charles
asked Nov 14 '18 at 19:31
CharlesCharles
4071032
4071032
I am not 100% clear what you want to achieve. Could you please give a description of an input and expected processing workflow?
– Bogumił Kamiński
Nov 15 '18 at 6:56
@BogumiłKamiński, I added a more concrete example of the functionality that I'm looking to achieve.
– Charles
Nov 15 '18 at 16:04
EDIT and EDIT 2 in my answer work with your example (I leave both of them as I think it is helpful to analyze the difference between them).
– Bogumił Kamiński
Nov 15 '18 at 22:09
add a comment |
I am not 100% clear what you want to achieve. Could you please give a description of an input and expected processing workflow?
– Bogumił Kamiński
Nov 15 '18 at 6:56
@BogumiłKamiński, I added a more concrete example of the functionality that I'm looking to achieve.
– Charles
Nov 15 '18 at 16:04
EDIT and EDIT 2 in my answer work with your example (I leave both of them as I think it is helpful to analyze the difference between them).
– Bogumił Kamiński
Nov 15 '18 at 22:09
I am not 100% clear what you want to achieve. Could you please give a description of an input and expected processing workflow?
– Bogumił Kamiński
Nov 15 '18 at 6:56
I am not 100% clear what you want to achieve. Could you please give a description of an input and expected processing workflow?
– Bogumił Kamiński
Nov 15 '18 at 6:56
@BogumiłKamiński, I added a more concrete example of the functionality that I'm looking to achieve.
– Charles
Nov 15 '18 at 16:04
@BogumiłKamiński, I added a more concrete example of the functionality that I'm looking to achieve.
– Charles
Nov 15 '18 at 16:04
EDIT and EDIT 2 in my answer work with your example (I leave both of them as I think it is helpful to analyze the difference between them).
– Bogumił Kamiński
Nov 15 '18 at 22:09
EDIT and EDIT 2 in my answer work with your example (I leave both of them as I think it is helpful to analyze the difference between them).
– Bogumił Kamiński
Nov 15 '18 at 22:09
add a comment |
1 Answer
1
active
oldest
votes
Is this what you want?
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
and now
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
Note that I have changed a
and b
definitions as your add!
required Float64
as a second argument.
EDIT: If you want to use this macro inside a function the simplest thing to do is to esc
its whole return value:
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
esc(quote
for j in 1:length(v)
$expr
end
end)
end
Then you can define e.g.:
function f()
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
v
end
and run f()
to get the same result as above in global scope.
EDIT 2: I just realized that actually I have to sanitize j
as otherwise the following code will fail:
test!(v_test, j, b_test) =
@vectorize(add!(v_test, j), add!(v_test, b_test))
Here is how you should do it:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, j)
expr = :($expr; $a)
end
esc(quote
for $j in 1:length(v)
$expr
end
end)
end
As you can see developing macros is a non-obvious task (hopefully the final recipe is bug-free :)).
EDIT 3: Here is the code that correctly handles length
. Also now in each expression actually you can pass a different value as a first argument (so you can independently process different vectors). If you do want to process the same vector check is a.args[2]
is always the same symbol:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
var = a.args[2]
push!(a.args, j)
q = quote
for $j in 1:length($var)
$a
end
end
expr = :($expr; $q)
end
esc(expr)
end
This is precisely what I need, except that I need the macro called within a function. I'll add a version of what I mean to the question.
– Charles
Nov 15 '18 at 20:12
I have added an example how to make all variables be resolved in macro call environment (includingj
- but in this case it is not a problem asj
is guaranteed to be local to thefor
loop anyway and will not leak out). In a more complex situation you might have to be more careful and escape out only the variables that need to be resolved in macro call environment.
– Bogumił Kamiński
Nov 15 '18 at 21:04
Actuallyj
should be sanitized - see my EDIT 2. Your example works cleanly now.
– Bogumił Kamiński
Nov 15 '18 at 22:08
This is close enough. The only thing that I would argue looks like is being cheated (which I suspect required some careful attention anyway) is thelength(v)
statement, which seems to use the scope for whichv
is defined. But I at least have an idea how this can be circumvented. Thanks!
– Charles
Nov 15 '18 at 22:44
1
This is easily changed - as you note. I did multiple loops on purpose to allow for possibility of varying size of the vector.
– Bogumił Kamiński
Nov 16 '18 at 19:45
|
show 6 more comments
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53307532%2fvectorizing-groups-of-function-calls%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Is this what you want?
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
and now
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
Note that I have changed a
and b
definitions as your add!
required Float64
as a second argument.
EDIT: If you want to use this macro inside a function the simplest thing to do is to esc
its whole return value:
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
esc(quote
for j in 1:length(v)
$expr
end
end)
end
Then you can define e.g.:
function f()
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
v
end
and run f()
to get the same result as above in global scope.
EDIT 2: I just realized that actually I have to sanitize j
as otherwise the following code will fail:
test!(v_test, j, b_test) =
@vectorize(add!(v_test, j), add!(v_test, b_test))
Here is how you should do it:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, j)
expr = :($expr; $a)
end
esc(quote
for $j in 1:length(v)
$expr
end
end)
end
As you can see developing macros is a non-obvious task (hopefully the final recipe is bug-free :)).
EDIT 3: Here is the code that correctly handles length
. Also now in each expression actually you can pass a different value as a first argument (so you can independently process different vectors). If you do want to process the same vector check is a.args[2]
is always the same symbol:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
var = a.args[2]
push!(a.args, j)
q = quote
for $j in 1:length($var)
$a
end
end
expr = :($expr; $q)
end
esc(expr)
end
This is precisely what I need, except that I need the macro called within a function. I'll add a version of what I mean to the question.
– Charles
Nov 15 '18 at 20:12
I have added an example how to make all variables be resolved in macro call environment (includingj
- but in this case it is not a problem asj
is guaranteed to be local to thefor
loop anyway and will not leak out). In a more complex situation you might have to be more careful and escape out only the variables that need to be resolved in macro call environment.
– Bogumił Kamiński
Nov 15 '18 at 21:04
Actuallyj
should be sanitized - see my EDIT 2. Your example works cleanly now.
– Bogumił Kamiński
Nov 15 '18 at 22:08
This is close enough. The only thing that I would argue looks like is being cheated (which I suspect required some careful attention anyway) is thelength(v)
statement, which seems to use the scope for whichv
is defined. But I at least have an idea how this can be circumvented. Thanks!
– Charles
Nov 15 '18 at 22:44
1
This is easily changed - as you note. I did multiple loops on purpose to allow for possibility of varying size of the vector.
– Bogumił Kamiński
Nov 16 '18 at 19:45
|
show 6 more comments
Is this what you want?
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
and now
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
Note that I have changed a
and b
definitions as your add!
required Float64
as a second argument.
EDIT: If you want to use this macro inside a function the simplest thing to do is to esc
its whole return value:
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
esc(quote
for j in 1:length(v)
$expr
end
end)
end
Then you can define e.g.:
function f()
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
v
end
and run f()
to get the same result as above in global scope.
EDIT 2: I just realized that actually I have to sanitize j
as otherwise the following code will fail:
test!(v_test, j, b_test) =
@vectorize(add!(v_test, j), add!(v_test, b_test))
Here is how you should do it:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, j)
expr = :($expr; $a)
end
esc(quote
for $j in 1:length(v)
$expr
end
end)
end
As you can see developing macros is a non-obvious task (hopefully the final recipe is bug-free :)).
EDIT 3: Here is the code that correctly handles length
. Also now in each expression actually you can pass a different value as a first argument (so you can independently process different vectors). If you do want to process the same vector check is a.args[2]
is always the same symbol:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
var = a.args[2]
push!(a.args, j)
q = quote
for $j in 1:length($var)
$a
end
end
expr = :($expr; $q)
end
esc(expr)
end
This is precisely what I need, except that I need the macro called within a function. I'll add a version of what I mean to the question.
– Charles
Nov 15 '18 at 20:12
I have added an example how to make all variables be resolved in macro call environment (includingj
- but in this case it is not a problem asj
is guaranteed to be local to thefor
loop anyway and will not leak out). In a more complex situation you might have to be more careful and escape out only the variables that need to be resolved in macro call environment.
– Bogumił Kamiński
Nov 15 '18 at 21:04
Actuallyj
should be sanitized - see my EDIT 2. Your example works cleanly now.
– Bogumił Kamiński
Nov 15 '18 at 22:08
This is close enough. The only thing that I would argue looks like is being cheated (which I suspect required some careful attention anyway) is thelength(v)
statement, which seems to use the scope for whichv
is defined. But I at least have an idea how this can be circumvented. Thanks!
– Charles
Nov 15 '18 at 22:44
1
This is easily changed - as you note. I did multiple loops on purpose to allow for possibility of varying size of the vector.
– Bogumił Kamiński
Nov 16 '18 at 19:45
|
show 6 more comments
Is this what you want?
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
and now
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
Note that I have changed a
and b
definitions as your add!
required Float64
as a second argument.
EDIT: If you want to use this macro inside a function the simplest thing to do is to esc
its whole return value:
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
esc(quote
for j in 1:length(v)
$expr
end
end)
end
Then you can define e.g.:
function f()
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
v
end
and run f()
to get the same result as above in global scope.
EDIT 2: I just realized that actually I have to sanitize j
as otherwise the following code will fail:
test!(v_test, j, b_test) =
@vectorize(add!(v_test, j), add!(v_test, b_test))
Here is how you should do it:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, j)
expr = :($expr; $a)
end
esc(quote
for $j in 1:length(v)
$expr
end
end)
end
As you can see developing macros is a non-obvious task (hopefully the final recipe is bug-free :)).
EDIT 3: Here is the code that correctly handles length
. Also now in each expression actually you can pass a different value as a first argument (so you can independently process different vectors). If you do want to process the same vector check is a.args[2]
is always the same symbol:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
var = a.args[2]
push!(a.args, j)
q = quote
for $j in 1:length($var)
$a
end
end
expr = :($expr; $q)
end
esc(expr)
end
Is this what you want?
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
and now
function add!(v::VectorFloat64, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::VectorFloat64, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
Note that I have changed a
and b
definitions as your add!
required Float64
as a second argument.
EDIT: If you want to use this macro inside a function the simplest thing to do is to esc
its whole return value:
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
esc(quote
for j in 1:length(v)
$expr
end
end)
end
Then you can define e.g.:
function f()
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
@vectorize(add!(v, a), add!(v, b))
v
end
and run f()
to get the same result as above in global scope.
EDIT 2: I just realized that actually I have to sanitize j
as otherwise the following code will fail:
test!(v_test, j, b_test) =
@vectorize(add!(v_test, j), add!(v_test, b_test))
Here is how you should do it:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, j)
expr = :($expr; $a)
end
esc(quote
for $j in 1:length(v)
$expr
end
end)
end
As you can see developing macros is a non-obvious task (hopefully the final recipe is bug-free :)).
EDIT 3: Here is the code that correctly handles length
. Also now in each expression actually you can pass a different value as a first argument (so you can independently process different vectors). If you do want to process the same vector check is a.args[2]
is always the same symbol:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
var = a.args[2]
push!(a.args, j)
q = quote
for $j in 1:length($var)
$a
end
end
expr = :($expr; $q)
end
esc(expr)
end
edited Nov 16 '18 at 6:23
answered Nov 15 '18 at 19:00
Bogumił KamińskiBogumił Kamiński
15.5k21422
15.5k21422
This is precisely what I need, except that I need the macro called within a function. I'll add a version of what I mean to the question.
– Charles
Nov 15 '18 at 20:12
I have added an example how to make all variables be resolved in macro call environment (includingj
- but in this case it is not a problem asj
is guaranteed to be local to thefor
loop anyway and will not leak out). In a more complex situation you might have to be more careful and escape out only the variables that need to be resolved in macro call environment.
– Bogumił Kamiński
Nov 15 '18 at 21:04
Actuallyj
should be sanitized - see my EDIT 2. Your example works cleanly now.
– Bogumił Kamiński
Nov 15 '18 at 22:08
This is close enough. The only thing that I would argue looks like is being cheated (which I suspect required some careful attention anyway) is thelength(v)
statement, which seems to use the scope for whichv
is defined. But I at least have an idea how this can be circumvented. Thanks!
– Charles
Nov 15 '18 at 22:44
1
This is easily changed - as you note. I did multiple loops on purpose to allow for possibility of varying size of the vector.
– Bogumił Kamiński
Nov 16 '18 at 19:45
|
show 6 more comments
This is precisely what I need, except that I need the macro called within a function. I'll add a version of what I mean to the question.
– Charles
Nov 15 '18 at 20:12
I have added an example how to make all variables be resolved in macro call environment (includingj
- but in this case it is not a problem asj
is guaranteed to be local to thefor
loop anyway and will not leak out). In a more complex situation you might have to be more careful and escape out only the variables that need to be resolved in macro call environment.
– Bogumił Kamiński
Nov 15 '18 at 21:04
Actuallyj
should be sanitized - see my EDIT 2. Your example works cleanly now.
– Bogumił Kamiński
Nov 15 '18 at 22:08
This is close enough. The only thing that I would argue looks like is being cheated (which I suspect required some careful attention anyway) is thelength(v)
statement, which seems to use the scope for whichv
is defined. But I at least have an idea how this can be circumvented. Thanks!
– Charles
Nov 15 '18 at 22:44
1
This is easily changed - as you note. I did multiple loops on purpose to allow for possibility of varying size of the vector.
– Bogumił Kamiński
Nov 16 '18 at 19:45
This is precisely what I need, except that I need the macro called within a function. I'll add a version of what I mean to the question.
– Charles
Nov 15 '18 at 20:12
This is precisely what I need, except that I need the macro called within a function. I'll add a version of what I mean to the question.
– Charles
Nov 15 '18 at 20:12
I have added an example how to make all variables be resolved in macro call environment (including
j
- but in this case it is not a problem as j
is guaranteed to be local to the for
loop anyway and will not leak out). In a more complex situation you might have to be more careful and escape out only the variables that need to be resolved in macro call environment.– Bogumił Kamiński
Nov 15 '18 at 21:04
I have added an example how to make all variables be resolved in macro call environment (including
j
- but in this case it is not a problem as j
is guaranteed to be local to the for
loop anyway and will not leak out). In a more complex situation you might have to be more careful and escape out only the variables that need to be resolved in macro call environment.– Bogumił Kamiński
Nov 15 '18 at 21:04
Actually
j
should be sanitized - see my EDIT 2. Your example works cleanly now.– Bogumił Kamiński
Nov 15 '18 at 22:08
Actually
j
should be sanitized - see my EDIT 2. Your example works cleanly now.– Bogumił Kamiński
Nov 15 '18 at 22:08
This is close enough. The only thing that I would argue looks like is being cheated (which I suspect required some careful attention anyway) is the
length(v)
statement, which seems to use the scope for which v
is defined. But I at least have an idea how this can be circumvented. Thanks!– Charles
Nov 15 '18 at 22:44
This is close enough. The only thing that I would argue looks like is being cheated (which I suspect required some careful attention anyway) is the
length(v)
statement, which seems to use the scope for which v
is defined. But I at least have an idea how this can be circumvented. Thanks!– Charles
Nov 15 '18 at 22:44
1
1
This is easily changed - as you note. I did multiple loops on purpose to allow for possibility of varying size of the vector.
– Bogumił Kamiński
Nov 16 '18 at 19:45
This is easily changed - as you note. I did multiple loops on purpose to allow for possibility of varying size of the vector.
– Bogumił Kamiński
Nov 16 '18 at 19:45
|
show 6 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53307532%2fvectorizing-groups-of-function-calls%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I am not 100% clear what you want to achieve. Could you please give a description of an input and expected processing workflow?
– Bogumił Kamiński
Nov 15 '18 at 6:56
@BogumiłKamiński, I added a more concrete example of the functionality that I'm looking to achieve.
– Charles
Nov 15 '18 at 16:04
EDIT and EDIT 2 in my answer work with your example (I leave both of them as I think it is helpful to analyze the difference between them).
– Bogumił Kamiński
Nov 15 '18 at 22:09