Change do item variable name?


I am curious is it possible to change the default item variable name of a do loop? {3 4 5 8}.select[item.pow2?]

Like in ruby {|enemy| enemy.alive?}

Asking because i tried a nested loop and it did not like that item is declared twice.

1 Like

Yea, I would rather we pass a lambda expression to these methods instead of using a implicit argument name.

Yes. Good discovery and good question. It actually is a closure (essentially a lambda expression or anonymous function) that is being passed.

Using the example that you give {3 4 5 8}.select[item.pow2?] the part in square brackets [item.pow2?] is a closure - a mini routine. A closure needs a parameter list just like other routines and if one isn’t provided it borrows the one used in the routine that it is being passed into - in this case the List@select() method. If you have the above example in the workspace of the SkookumIDE, place the edit insertion point caret anywhere on the select and press Alt + G and it will pop you to the definition of the List@select() method in the SkookumIDE Browser.

As you can see in code for select(), the parameters are:

((ItemClass_ item) Boolean test) ThisClass_

So the first parameter says that it expects a closure object that has a list object called item and that it should return a Boolean true or false result. So this is where the default item comes from. When you don’t specify a parameter list it uses this one.

Here is the same example with the parameter list specified using the variable num instead of item:

{3 4 5 8}.select (Integer num)[num.pow2?]

Currently you must provide some whitespace between the select routine call and the start of the parameter list for the closure otherwise the parser thinks the brackets are part of the call to select. This can even be a different line.

{3 4 5 8}.select
  (Integer num)[num.pow2?]

If you really want, you can also put in the brackets for the select call:

{3 4 5 8}.select((Integer num)[num.pow2?])

Though usually it looks nicer to omit them.

SkookumScript does not allow nested scopes to reuse the same variable names and hiding the earlier variables since this often leads to horrible bugs that are hard to find. I didn’t think of the annoyance of nesting calls to methods like do() and select() though I still think not allowing scoped name hiding is the way to go. There might be a mechanism to auto increment nested names such as item, item2, item3, … though I would have to think on that and there is the issue of making magic names. It is already confusing enough to figure out where the name item comes from in the first place.

So here is an example of nested looping.

{{3 4 5 8} {6 8 11 16}}.do[[item.pow2?]]

This causes a parse error since there are successive item variables in nested scopes. To fix the issue just specify one of the variables explicitly.

Either for the call to select():

{{3 4 5 8} {6 8 11 16}}.do[ (Integer num)[num.pow2?]]

Or the call to do()

{{3 4 5 8} {6 8 11 16}}.do (List{Integer} list)[[item.pow2?]]

Some additional discussion on closures can be found here:

I hope this makes sense. Let me know if you have any additional questions or if you have ideas on how to make this mechanism even better. :madsci:

I think how you have it is fine and is what I would expect. I just could not figure out the syntax - the syntax doc page is hard to understand. I tried the parentheses syntax but was missing the whitespace.

I don’t think you should auto-magically name nested variable names.

thanks again.

I agree that needing the whitespace is not intuitive. I’ll see if I can think of a way around it. If you have any ideas please let me know.