On 5/20/06, Daniel S. [email protected] wrote:
Daniel S. wrote:
List inject := method(n, blk, self foreach(v, n = blk call(n, v)))
Found out what the problem was – the ‘call’ message to ‘blk’ messed
things up. This works:
List inject := method(n, blk, self foreach(v, n = blk(n, v)))
list(1, 2, 3) => 6
I’ve written this one quite a few times and ways in Io. The way you
write it heavily depends on the features you want and which idioms you
want to use. My first attempt at it was to mirror Ruby’s inject as
close as possible (updated to work with the latest darcs at
iolanguage.com):
List inject := method(
lst := self clone
index := 0
if(call argCount == 3,
memo := lst removeFirst
index = index - 1
,
memo := call evalArgAt(0)
)
memoName := call argAt(index + 1) name
varName := call argAt(index + 2) name
body := call argAt(index + 3)
yield := block setMessage(body)
setArgumentNames(list(memoName,varName)) setScope(call sender)
lst foreach(elem, memo = yield(memo, elem))
memo
)
Usage
list(1,2,3) inject(0,m,v, m+v)
list(3,2,1) inject(m,v, m+v)
Note that it is a bit complex looking because I allow it to work for
calls with and calls without a seed value. This mirrors the ruby
philosophy quite close as you might note that the inject code is
called with a Block object that I construct (calling it yield even).
It also creates the lexical closure and a block local scope like ruby
would have. I prefer this in general but it is NOT considered
idiomatic Io (at least at this point).
The main thing Io does away with is scoping the code in its own block.
Instead, most code is directly evaluated in the scope of the sender.
This is simpler to write but can cause problems if you are not aware
of this difference. In the end it comes down to a stylistic issue (I
tend to lean more towards FP so I don’t like such broad side effects).
A sample implementation might look like this:
List inject := method(
lst := self clone
index := 0
if(call argCount == 3,
memo := lst removeFirst
index = index - 1
,
memo := call evalArgAt(0)
)
memoName := call argAt(index + 1) name
varName := call argAt(index + 2) name
bodyIndex := index + 3
lst foreach(elem,
call sender setSlot(memoName, memo)
call sender setSlot(varName, elem)
memo = call evalArgAt(bodyIndex)
)
memo
)
Of course, most of the code is the same (most of it is for dealing
with optional seed values anyway). This time you can see that I am
evaluating the messages in a very specific context rather than
creating a high level calling mechanism. This mirrors a lot of Io (for
many reasons outside the scope of this email).
In the end I usually end up writing with my own style because many
idioms in Io are still immature. Things are slowly developing but
there are still things to be worked out (IMO – some people refuse to
admit fault on some things). I highly recommend you join the ML (no
yahoo account needed – find the subscribe email addr. and just send a
mail to it) if you want to listen in. In the archives you might find
some interesting threads. Related to this one might be the IoProp1
thread that deals with activation of objects which heavily determines
how easy it is to build correct code that uses high order procedures
(hint: both of the above have a bug in it related to activatable
objects… I left it in for clarity as the solution muddles the
solutions a bit more than I wanted).
Brian.