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://eigenclass.org - singular Ruby