Bind and Destructure block arguments
I'd want a solution that assigned [[1, 2], [3, 4]] to x, [1, 2] to y and [3, 4] to z
How about this:
irb(main):001:0> y, z = x = [[1, 2], [3, 4]]
=> [[1, 2], [3, 4]]
irb(main):002:0> y
=> [1, 2]
irb(main):003:0> z
=> [3, 4]
irb(main):004:0> x
=> [[1, 2], [3, 4]]
Update (Let's put this into a block)
Is this okey for you:
[[1, 2], [3, 4]].tap do |arr|
y, z = x = arr
p x # => [[1, 2], [3, 4]]
p y # => [1, 2]
p z # => [3, 4]
end
Destructuring Ruby block arguments dynamically
In your specific case, you'd use a splat to collect up everything but the first value:
triples.each { |first, *rest| puts first }
#-----------------------^splat
The *rest
notation just collects everything that's left into an Array called rest
.
In general, there's not much point to creating an arbitrary number of local variables (second, third, ..., nth
) because you wouldn't be able to do anything with them; you could probably whip up a sick mess of evals but we already have a fine and functional Array class so why bother?
Destructuring a variadic function with an as-binding: not possible?
If you want a variable number of args, you are missing an &:
(defn foo [& [x & more :as full-list]]
(println "x:" x)
(println "more:" more)
(println "full list:" full-list))
Clojure param definition has only one special case that is the &
char to indicate a variadic number of arguments. The rest are plain simple named arguments.
Now each simple argument can be destructured using the map or list syntax. For example:
(defn foo [ x y ] ...)
Can be destructured like:
(defn foo [[x1 x2 & x-more :as x] {:keys [y1 y2 y3]}] ...)
So we are saying that we expect the first param to be a list of at least 2 elements and the second param to be a map with some keys. Note that this will still be a fn of two params and that Clojure is not going to enforce that x actually has at least two elements. If x is an empty list, x1 and x2 will be nil.
Coming back to your question, if you look at my answer you will see that my fn has 0 mandatory params, with a variable number of arguments, while the one that you have has 1 mandatory param with a a variable number of arguments. What I am doing is just destructuring the var arg.
Number of arguments in a ruby block
This works because Ruby supports destructuring.
Destructuring allows you to bind a set of variables to a corresponding set of values anywhere that you can normally bind a value to a single variable.
This allows the following to hold true:
arr = [1, 2]
x = arr
x == [1, 2] # true
y, z = arr
y == 1 # true
z == 2 # true
You can see from the following code that destructuring in arguments to blocks isn't unique to the built-in methods that take a block:
def my_method(arr)
yield arr
end
my_method([1, 2, 3]) {|x| puts x.inspect }
# => [1, 2, 3]
my_method([1, 2, 3]) {|x, y, z| puts x.inspect }
# => 1
Check out Destructuring with Ruby for more information.
How does Ruby Array #count handle multiple block arguments
Just like assignments, there's a (not-so-) secret shortcut. If the right-hand-side is an array and the left-hand-side has multiple variables, the array is splatted, so the following two lines are identical:
a, b, c = [1, 2, 3]
a, b, c = *[1, 2, 3]
While not the same thing, blocks have something in the same vein, when the yielded value is an array, and there are multiple parameters. Thus, these two blocks will act the same when you yield [1, 2, 3]
:
do |a, b, c|
...
end
do |(a, b, c)|
...
end
So, in your case, the value gets deconstructed, as if you wrote this:
[[1,1], [2,2], [3,4]].count {|(a,b)| a != b} # => 1
If you had another value that you are passing along with the array, you would have to specify the structure explicitly, as the deconstruction of the array would not be automatic in the way we want:
[[1,1], [2,2], [3,4]].each.with_index.count {|e,i| i + 1 == e[1] }
# automatic deconstruction of [[1,1],0]:
# e=[1,1]; i=0
[[1,1], [2,2], [3,4]].each.with_index.count {|(a,b),i| i + 1 == b }
# automatic deconstruction of [[1,1],0], explicit deconstruction of [1,1]:
# a=1; b=1; i=0
[[1,1], [2,2], [3,4]].each.with_index.count {|a,b,i| i + 1 == b }
# automatic deconstruction of [[1,1],0]
# a=[1,1]; b=0; i=nil
# NOT what we want
Arguments to Class.new block
It turns out that the block argument is the class you are creating. Run this snippet in irb
Class.new do |what|
p what
end
and you will see something like
#<Class:0x000000022b2698>
=> #<Class:0x000000022b2698>
The first line of output is given by p what
, and the second line shows the return value of Class.new
, which we know is the class. You can see that the what
is the same object as the return value of Class.new
.
Conclusion: the block argument is not very useful because you can get the class instance just using self
in that class. The only usage I can imagine of is using the trick called flat scope to create methods.
Foo = Class.new do |klass|
define_method :class_name do
klass.name
end
end
Foo.new.class_name #=> "Foo"
Yet this is not very useful either because an object can easily access its class with self.class
.
ruby: name of this syntax, which splits positional argument of block
Yes, it is called destructuring.
So what is destructuring? The most concise definition I found is from Common Lisp the Language. Destructuring allows you to bind a set of variables to a corresponding set of values anywhere that you can normally bind a value to a single variable. It is a powerful feature of Clojure that lets you write some very elegant code. For more information about Clojure's features, I recommend you check out Jay Field's blog post on the subject. While destructuring in Ruby is not quite as powerful as Clojure, you can still do some cool stuff.
Why does destructuring an argument object affect it's 'this' value?
In the first case, by destructuring you've created a separate reference to the sayHi
function. When you then call it from the body of foo, this becomes the context for this
in the body of sayHi
.
In the second case, you are calling it as person.sayHi
, so the context in this case is the containing object - i.e. person
. Within sayHi
, this
is person
, and name is defined.
There's nothing magical about destructuring, if you manually create a reference to the function you'll get the same effect:
const bar = person => {
const sayHi = person.sayHi;
console.log(sayHi());
};
Why no destructing in def form?
def
is a special form at the compiler level: it makes a Var. def
has to be available and usable before destructuring is available. You see something similar with let*
, a compiler primitive that supports no destructuring: then after several thousand lines in clojure/core.clj
the language is finally powerful enough to provide a version of let
with destructuring, as a macro on top of let*
.
If you want, you can write a macro (say, def+
) that does this for you. Personally I think it's kinda gross and wouldn't use it, but using a Lisp means getting to use a language that suits you personally.
Related Topics
Can't Find Ffi.H When Installing Ffi Ruby Gem
Illegal Quoting in Line 1 Using Ruby CSV
Rails Form_For Never Invokes the Create Controller Action to Use Redirect_To
Selenium Can't Find Fields with Type Number
Knowing When Resque Worker Had Completed Job
How to Make the Say Command Echo a Variable Value in a Script
Adding a Product Using Savon to Connect to Magento API
Pass Ruby Script File to Rails Console
Intermingling Attr_Accessor and an Initialize Method in One Class
Numeric Literals Prepended with '0'
What Ruby Features Are Used in Chef Recipes
Ruby Webrick Http Authentication
Running Rake Task from Within War File
Ruby Before_Validation Triggers Infinite Loop of Call Back