问题
The following produces a compilation error.
to-report call-first/last [first/last a-list]
report first/last a-list
end
a-list
in the body is highlighted with the error: Expected command.
So I tried the following.
to-report call-first/last [first/last a-list]
report first map first/last (list a-list)
end
This code compiled(!), but when I attempted to have the observer execute it
call-first/last first [1 2 3]
call-first/last
was highlighted with the error message: CALL-FIRST/LAST expected 2 inputs.
Any thoughts about an alternative approach?
Thanks.
P.S. The bigger picture is to find a way (if there is one) to write a higher-order function in NetLogo.
UPDATE:
Asking the observer to execute the following works properly!
call-first-last [[a-list] -> first a-list] [1 2 3]
;; => 1
call-first-last [[a-list] -> last a-list] [1 2 3]
;; => 3
But it sure is a long way around!
Another UPDATE:
Tried
to-report call-first-last [first-last a-list]
report first map (runresult first-last) (list a-list)
end
This compiled.
But asking the observer to run call-first-last "first" [1 2 3]
produced a Runtime Error: FIRST expected 1 input
So the question seems to come down to whether there is a way (other than using an anonymous expression as above) to write an expression that returns a function?
回答1:
I'm not sure I have all the details correct, but my first thought when I saw what you were trying to do is to use runresult
. This works:
to-report call-first-last [first-last a-list]
report runresult word first-last a-list
end
With (for example): call-first-last "last" [1 2 3]
You would need to add some brackets for both word
and runresult
as you do more complicated expressions with more than two terms.
回答2:
So the question seems to come down to whether there is a way (other than using an anonymous expression as above) to write an expression that returns a function?
And the answer comes down to... no.
When you tried writing call-first/last first [1 2 3]
, you were trying to use the "concise syntax" for passing procedures to higher-order procedures: passing the name of the procedure directly instead of passing an expression like [ xs -> first xs ]
. (You can read a bit about it in the programming guide.)
This syntax works with built-in NetLogo primitives like map
and foreach
, but not for user-defined procedures. Without getting too formal, the reason for this is that NetLogo primitives' arguments are typed: NetLogo knows that the map
expects a reporter as its first argument, so it can parse a call like map first my-list-of-lists
correctly. User-written code is not like that: NetLogo has know idea what the type of the first/last
argument of your call-first/last
procedure is supposed to be.
The standard way to deal with this is to bite the bullet and just write something like call-first-last [xs -> first xs] [1 2 3]
.
If that really bothers you, you can use Jen's solution (though it would be a bit slower).
You could also write an extension. NetLogo extensions allow you to write primitives that behave like native NetLogo procedures, and thus support the concise syntax. It might be worth it if you have a whole set of procedures like this, but otherwise, that's a whole lot of trouble just to save a few characters.
(Side note: it's up to you, of course, but I would avoid using /
in NetLogo variable names. The language allows it, yes, but for pretty much anyone looking at your code, it looks a heck of a lot like a division...)
来源:https://stackoverflow.com/questions/62522535/attempting-to-trick-netlogo-into-executing-a-function-passed-as-an-argument