[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Underscore

Jon Harrop

5/29/2007 6:49:00 AM

Logan Capaldo wrote:
>> > However, I'd probably write it like:
>> > def nest(x, n = 2)
>> > (1..n).inject(x) { |acc, _| yield(acc) }
>> > end
>>
>> I don't understand this one. I think "inject" is a fold and "yield"
>> returns a value and a continuation. Looks like the continuation is
>> ignored the next time it is accumulated, but won't the result have a
>> continuation in it?
> inject is a fold. yield is not a continuation, but rather a way of
> accessing the passed in function (block) anonymously.
>
> def f
> yield
> end
>
> def f1(&b)
> b.call
> end
>
> f { puts "Does the same thing" }
> f1 { puts "Does the same thing" }

I see. So that was equivalent to:

let rec nest ?(n=2) x f =
Seq.fold (fun acc -> f acc) x {1 .. n}

but what is the meaning of the "_" in the Ruby "|acc, _|"?

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/products/fsharp_journ...
19 Answers

Michael Fellinger

5/29/2007 7:17:00 AM

0

some people use _ as a temporary meaningless variable, just a
throw-away so to say.
In this case something like

hash = {:a => :b, :c => :d}

and you are not interested in the :b and :d

hash.each do |key, _|
p key
end

I'm not necessarily a friend of this technique, but it seems easy on
the minds of some people.

^ manveru

On 5/29/07, Jon Harrop <jon@ffconsultancy.com> wrote:
> Logan Capaldo wrote:
> >> > However, I'd probably write it like:
> >> > def nest(x, n = 2)
> >> > (1..n).inject(x) { |acc, _| yield(acc) }
> >> > end
> >>
> >> I don't understand this one. I think "inject" is a fold and "yield"
> >> returns a value and a continuation. Looks like the continuation is
> >> ignored the next time it is accumulated, but won't the result have a
> >> continuation in it?
> > inject is a fold. yield is not a continuation, but rather a way of
> > accessing the passed in function (block) anonymously.
> >
> > def f
> > yield
> > end
> >
> > def f1(&b)
> > b.call
> > end
> >
> > f { puts "Does the same thing" }
> > f1 { puts "Does the same thing" }
>
> I see. So that was equivalent to:
>
> let rec nest ?(n=2) x f =
> Seq.fold (fun acc -> f acc) x {1 .. n}
>
> but what is the meaning of the "_" in the Ruby "|acc, _|"?
>
> --
> Dr Jon D Harrop, Flying Frog Consultancy
> The F#.NET Journal
> http://www.ffconsultancy.com/products/fsharp_journ...
>
>

Jon Harrop

5/29/2007 10:46:00 AM

0

Michael Fellinger wrote:
> some people use _ as a temporary meaningless variable, just a
> throw-away so to say.
> In this case something like
>
> hash = {:a => :b, :c => :d}
>
> and you are not interested in the :b and :d
>
> hash.each do |key, _|
> p key
> end
>
> I'm not necessarily a friend of this technique, but it seems easy on
> the minds of some people.

Right, this is exactly what I guessed it was doing (it is the same in
SML/OCaml/F#) but what value was being thrown away in the Ruby program and
where did it come from?

(1..n).inject(x) { |acc, _| yield(acc) }

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/products/fsharp_journ...

Jano Svitok

5/29/2007 11:07:00 AM

0

On 5/29/07, Jon Harrop <jon@ffconsultancy.com> wrote:
> Michael Fellinger wrote:
> > some people use _ as a temporary meaningless variable, just a
> > throw-away so to say.
> > In this case something like
> >
> > hash = {:a => :b, :c => :d}
> >
> > and you are not interested in the :b and :d
> >
> > hash.each do |key, _|
> > p key
> > end
> >
> > I'm not necessarily a friend of this technique, but it seems easy on
> > the minds of some people.
>
> Right, this is exactly what I guessed it was doing (it is the same in
> SML/OCaml/F#) but what value was being thrown away in the Ruby program and
> where did it come from?
>
> (1..n).inject(x) { |acc, _| yield(acc) }
>
> --
> Dr Jon D Harrop, Flying Frog Consultancy
> The F#.NET Journal
> http://www.ffconsultancy.com/products/fsharp_journ...

inject takes a block with two parameters. classic example is a sum of an array:

array.inject(0) {|sum, item| sum + item }

so, in this case, item is not needed, so it is replaced by a variable
with name of "_"
that by convention means "temporary", "throw away"

it can be anything else:
(1..n).inject(x) { |acc, i_dont_need_this| yield(acc) }

Robert Klemme

5/29/2007 11:12:00 AM

0

On 29.05.2007 13:07, Jano Svitok wrote:
> On 5/29/07, Jon Harrop <jon@ffconsultancy.com> wrote:
>> Michael Fellinger wrote:
>> > some people use _ as a temporary meaningless variable, just a
>> > throw-away so to say.
>> > In this case something like
>> >
>> > hash = {:a => :b, :c => :d}
>> >
>> > and you are not interested in the :b and :d
>> >
>> > hash.each do |key, _|
>> > p key
>> > end
>> >
>> > I'm not necessarily a friend of this technique, but it seems easy on
>> > the minds of some people.
>>
>> Right, this is exactly what I guessed it was doing (it is the same in
>> SML/OCaml/F#) but what value was being thrown away in the Ruby program
>> and
>> where did it come from?
>>
>> (1..n).inject(x) { |acc, _| yield(acc) }
>>
>> --
>> Dr Jon D Harrop, Flying Frog Consultancy
>> The F#.NET Journal
>> http://www.ffconsultancy.com/products/fsharp_journ...
>
> inject takes a block with two parameters. classic example is a sum of an
> array:
>
> array.inject(0) {|sum, item| sum + item }
>
> so, in this case, item is not needed, so it is replaced by a variable
> with name of "_"
> that by convention means "temporary", "throw away"
>
> it can be anything else:
> (1..n).inject(x) { |acc, i_dont_need_this| yield(acc) }

Actually, #inject does not really make sense in this case. All that
happens here is that some value is yielded n times to a block. That
could have been done much more concise like this:

n.times { yield x }

Kind regards

robert

Chris Carter

5/29/2007 11:59:00 AM

0

On 5/29/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> On 29.05.2007 13:07, Jano Svitok wrote:
> > On 5/29/07, Jon Harrop <jon@ffconsultancy.com> wrote:
> >> Michael Fellinger wrote:
> >> > some people use _ as a temporary meaningless variable, just a
> >> > throw-away so to say.
> >> > In this case something like
> >> >
> >> > hash = {:a => :b, :c => :d}
> >> >
> >> > and you are not interested in the :b and :d
> >> >
> >> > hash.each do |key, _|
> >> > p key
> >> > end
> >> >
> >> > I'm not necessarily a friend of this technique, but it seems easy on
> >> > the minds of some people.
> >>
> >> Right, this is exactly what I guessed it was doing (it is the same in
> >> SML/OCaml/F#) but what value was being thrown away in the Ruby program
> >> and
> >> where did it come from?
> >>
> >> (1..n).inject(x) { |acc, _| yield(acc) }
> >>
> >> --
> >> Dr Jon D Harrop, Flying Frog Consultancy
> >> The F#.NET Journal
> >> http://www.ffconsultancy.com/products/fsharp_journ...
> >
> > inject takes a block with two parameters. classic example is a sum of an
> > array:
> >
> > array.inject(0) {|sum, item| sum + item }
> >
> > so, in this case, item is not needed, so it is replaced by a variable
> > with name of "_"
> > that by convention means "temporary", "throw away"
> >
> > it can be anything else:
> > (1..n).inject(x) { |acc, i_dont_need_this| yield(acc) }
>
> Actually, #inject does not really make sense in this case. All that
> happens here is that some value is yielded n times to a block. That
> could have been done much more concise like this:
>
> n.times { yield x }
>
> Kind regards
>
> robert
>
>

Actually, If it does what I think it does, the inject is needed
because as we know, inject sets the value returned from the block as
the accumulator for the next round. Therefore:
>> nest(2) {|x| p x; [22] }
2
[22]
=> [22]


--
Chris Carter
concentrationstudios.com
brynmawrcs.com

Robert Klemme

5/29/2007 12:11:00 PM

0

On 29.05.2007 13:59, Chris Carter wrote:
> On 5/29/07, Robert Klemme <shortcutter@googlemail.com> wrote:
>> On 29.05.2007 13:07, Jano Svitok wrote:
>> > On 5/29/07, Jon Harrop <jon@ffconsultancy.com> wrote:
>> >> Michael Fellinger wrote:
>> >> > some people use _ as a temporary meaningless variable, just a
>> >> > throw-away so to say.
>> >> > In this case something like
>> >> >
>> >> > hash = {:a => :b, :c => :d}
>> >> >
>> >> > and you are not interested in the :b and :d
>> >> >
>> >> > hash.each do |key, _|
>> >> > p key
>> >> > end
>> >> >
>> >> > I'm not necessarily a friend of this technique, but it seems easy on
>> >> > the minds of some people.
>> >>
>> >> Right, this is exactly what I guessed it was doing (it is the same in
>> >> SML/OCaml/F#) but what value was being thrown away in the Ruby program
>> >> and
>> >> where did it come from?
>> >>
>> >> (1..n).inject(x) { |acc, _| yield(acc) }
>> >>
>> >> --
>> >> Dr Jon D Harrop, Flying Frog Consultancy
>> >> The F#.NET Journal
>> >> http://www.ffconsultancy.com/products/fsharp_journ...
>> >
>> > inject takes a block with two parameters. classic example is a sum
>> of an
>> > array:
>> >
>> > array.inject(0) {|sum, item| sum + item }
>> >
>> > so, in this case, item is not needed, so it is replaced by a variable
>> > with name of "_"
>> > that by convention means "temporary", "throw away"
>> >
>> > it can be anything else:
>> > (1..n).inject(x) { |acc, i_dont_need_this| yield(acc) }
>>
>> Actually, #inject does not really make sense in this case. All that
>> happens here is that some value is yielded n times to a block. That
>> could have been done much more concise like this:
>>
>> n.times { yield x }
>>
>> Kind regards
>>
>> robert
>>
>>
>
> Actually, If it does what I think it does, the inject is needed
> because as we know, inject sets the value returned from the block as
> the accumulator for the next round. Therefore:
>>> nest(2) {|x| p x; [22] }
> 2
> [22]
> => [22]

Stupid me. Of course you are right. I should have taken more time to
digest this - or have more coffee. Thank you for correcting me!

Kind regards

robert

Robert Klemme

5/29/2007 12:22:00 PM

0

On 29.05.2007 14:11, Robert Klemme wrote:
> On 29.05.2007 13:59, Chris Carter wrote:
>> On 5/29/07, Robert Klemme <shortcutter@googlemail.com> wrote:
>>> On 29.05.2007 13:07, Jano Svitok wrote:
>>> > On 5/29/07, Jon Harrop <jon@ffconsultancy.com> wrote:
>>> >> Michael Fellinger wrote:
>>> >> > some people use _ as a temporary meaningless variable, just a
>>> >> > throw-away so to say.
>>> >> > In this case something like
>>> >> >
>>> >> > hash = {:a => :b, :c => :d}
>>> >> >
>>> >> > and you are not interested in the :b and :d
>>> >> >
>>> >> > hash.each do |key, _|
>>> >> > p key
>>> >> > end
>>> >> >
>>> >> > I'm not necessarily a friend of this technique, but it seems
>>> easy on
>>> >> > the minds of some people.
>>> >>
>>> >> Right, this is exactly what I guessed it was doing (it is the same in
>>> >> SML/OCaml/F#) but what value was being thrown away in the Ruby
>>> program
>>> >> and
>>> >> where did it come from?
>>> >>
>>> >> (1..n).inject(x) { |acc, _| yield(acc) }
>>> >>
>>> >> --
>>> >> Dr Jon D Harrop, Flying Frog Consultancy
>>> >> The F#.NET Journal
>>> >> http://www.ffconsultancy.com/products/fsharp_journ...
>>> >
>>> > inject takes a block with two parameters. classic example is a sum
>>> of an
>>> > array:
>>> >
>>> > array.inject(0) {|sum, item| sum + item }
>>> >
>>> > so, in this case, item is not needed, so it is replaced by a variable
>>> > with name of "_"
>>> > that by convention means "temporary", "throw away"
>>> >
>>> > it can be anything else:
>>> > (1..n).inject(x) { |acc, i_dont_need_this| yield(acc) }
>>>
>>> Actually, #inject does not really make sense in this case. All that
>>> happens here is that some value is yielded n times to a block. That
>>> could have been done much more concise like this:
>>>
>>> n.times { yield x }
>>>
>>> Kind regards
>>>
>>> robert
>>>
>>>
>>
>> Actually, If it does what I think it does, the inject is needed
>> because as we know, inject sets the value returned from the block as
>> the accumulator for the next round. Therefore:
>>>> nest(2) {|x| p x; [22] }
>> 2
>> [22]
>> => [22]
>
> Stupid me. Of course you are right. I should have taken more time to
> digest this - or have more coffee. Thank you for correcting me!

I was too fast (again). Even though the return value is used, I'd rather do

n.times { x = yield x }

than using #inject which does more than needed in this case. :-)

Now, did I look at all aspects...?

Kind regards

robert

Mauricio Fernández

5/29/2007 12:41:00 PM

0

On Tue, May 29, 2007 at 07:55:05PM +0900, Jon Harrop wrote:
> Michael Fellinger wrote:
> > some people use _ as a temporary meaningless variable, just a
> > throw-away so to say.
> > In this case something like
> >
> > hash = {:a => :b, :c => :d}
> >
> > and you are not interested in the :b and :d
> >
> > hash.each do |key, _|
> > p key
> > end
> >
> > I'm not necessarily a friend of this technique, but it seems easy on
> > the minds of some people.
>
> Right, this is exactly what I guessed it was doing (it is the same in
> SML/OCaml/F#) but what value was being thrown away in the Ruby program and
> where did it come from?
>
> (1..n).inject(x) { |acc, _| yield(acc) }

You can read that as

let l = range 1 n in
List.fold_left (fun acc _ -> f acc) (List.hd l) (List.tl l)

f being the function corresponding to the implicit block called by yield, and
range : int -> int -> int list.

Actually, in Ruby 1..n is a Range object which responds to the #inject message
without creating an intermediate array, and the #inject method is implemented
elsewhere, so the above works a bit like this:


let val_of = function
Some x -> x
| None -> failwith "val_of"

(* In Ruby, Enumerable is a module that can be included ("mixin") into
* classes that define an #each method. It provides many useful methods like
* #map, #find, #find_all, #reject, #max, #min, #sort, #sort_by, #partition,
* #each_with_index, #include?... built atop #each.*)
class virtual ['value] enumerable =
object(self: 'b)
method virtual each : ('value -> unit) -> 'b (* returning self allows to
* chain method calls *)

method inject :
(* Ruby behaves like
* 'acc. ?first_value:'acc -> ('acc -> 'value -> 'acc) -> 'acc =
* but this doesn't type in ocaml since we'll pass the first
* value (:'value) to f if no first_value is given, forcing
* 'acc = 'value.
*)
?first_value:'value -> ('value -> 'value -> 'value) -> 'value =
fun ?first_value f ->
let acc = ref first_value in
(* written this way to mimic Ruby's implementation,
* which uses an accumulator initialized to Qundef *)
ignore (self#each (fun x ->
match !acc with
None -> acc := Some x
| Some v -> acc := Some (f v x)));
val_of !acc
end

class ['value] range (first : 'value) (last : 'value) succ inclusive =
object(self)
inherit ['value] enumerable
(* this gives us lots of methods implemented using #each *)

val upper_bound : 'value = if inclusive then succ last else last

method each f =
let rec loop v =
if v < upper_bound then (f v; loop (succ v))
in
loop first;
self
end

let _ =
(* r = 1..10 *)
let r = new range 1 10 succ true in
(* r = 1...10 would be new range 1 10 succ false *)
Printf.printf "%d %d\n"
(* r.inject{|s,_| s * 2} *)
(r#inject (fun s _ -> s * 2))
(* r.inject(10){|s,_| s * 2} *)
(r#inject ~first_value:10 (fun s _ -> s * 2))


Of course, being dynamically typed, Ruby doesn't have/need parameterized
classes, and instead of using a succ function, Range#each repeatedly calls the
#succ method of the lower bound.

One last note: while things like #inject are as powerful as their OCaml
counterparts (and more convenient thanks to dynamic typing, but you know
there's a price for that...), they are often slower than simpler iteration
methods:

require 'benchmark'

Benchmark.bm(10) do |bm|
a = (0..10000).to_a # create an array with values 0 to 10000

bm.report("each") do
100.times { sum = 0; a.each{|x| sum += x}; sum }
end

bm.report("inject") { 100.times { a.inject{|s,x| s + x} } }
end

# >> user system total real
# >> each 0.610000 0.000000 0.610000 ( 0.618960)
# >> inject 1.800000 0.020000 1.820000 ( 1.850604)
(yes, it really is that slow)

Also, Ruby doesn't optimize tail calls, so you cannot write the sort of
recursive functions OCaml excels at. OTOH, the core classes provide much more
functionality, and more often than not the existing higher-order functions
will fit the bill.

--
Mauricio Fernandez - http://eige... - singular Ruby

Jon Harrop

5/30/2007 2:04:00 AM

0

Mauricio Fernandez wrote:
> You can read that as
>
> let l = range 1 n in
> List.fold_left (fun acc _ -> f acc) (List.hd l) (List.tl l)

Oh, of course! The ignored argument is the number 1 .. n.

> Actually, in Ruby 1..n is a Range object which responds to the #inject
> message without creating an intermediate array,

Laziness, yep.

> ...
> class ...

You wouldn't use objects to do this in OCaml though. If you wanted to fold
over a data structure you'd just write:

let nest n f x = fold (fun x _ -> f x) x {1 .. n}

> Of course, being dynamically typed, Ruby doesn't have/need parameterized
> classes, and instead of using a succ function, Range#each repeatedly calls
> the #succ method of the lower bound.

I don't think parameterized classes are needed here.

If you want it more dynamic (generic over kind of data structure, for
example), you might write:

let nest n f x = {1 .. n}#fold (fun x _ -> f x) x

but I've never used this style in practice (you always know what data
structure you're dealing with).

> One last note: while things like #inject are as powerful as their OCaml
> counterparts (and more convenient thanks to dynamic typing, but you know
> there's a price for that...), they are often slower than simpler iteration
> methods:

There is a similar overhead in OCaml (for polymorphic HOFs).

> # >> user system total real
> # >> each 0.610000 0.000000 0.610000 ( 0.618960)
> # >> inject 1.800000 0.020000 1.820000 ( 1.850604)
> (yes, it really is that slow)
>
> Also, Ruby doesn't optimize tail calls, so you cannot write the sort of
> recursive functions OCaml excels at. OTOH, the core classes provide much
> more functionality, and more often than not the existing higher-order
> functions will fit the bill.

I think this is equivalent:

# let time f x =
let t = Sys.time() in
let f_x = f x in
Printf.printf "Time: %f\n%!" (Sys.time() -. t);
f_x;;
val time : ('a -> 'b) -> 'a -> 'b = <fun>
# let rec loop n f x = if n>0 then (ignore(f x); loop (n-1) f x);;
val loop : int -> ('a -> 'b) -> 'a -> unit = <fun>
# let a = Array.init 10000 (fun i -> i);;
....
# time (loop 100 (fun a -> Array.fold_left (+) 0 a)) a;;
Time: 0.203969
- : unit = ()

So the interpreted OCaml bytecode is ~6x faster and compiled OCaml is ~260x
faster.

Thanks for all the help!

--
Dr Jon D Harrop, Flying Frog Consultancy
The F#.NET Journal
http://www.ffconsultancy.com/products/fsharp_journ...

Brian Candler

5/30/2007 7:28:00 AM

0

On Wed, May 30, 2007 at 07:50:05AM +0900, Logan Capaldo wrote:
> On 5/29/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> >
> >
> >I was too fast (again). Even though the return value is used, I'd rather
> >do
> >
> >n.times { x = yield x }
> >
> >than using #inject which does more than needed in this case. :-)
> >
> >Now, did I look at all aspects...?
>
>
> Almost.
> you'd actually need
> n.times { x = yield x }
> x

Almost almost :-)

x = nil
n.times { x = yield x }
x