Passing procs and methods - ruby
I came across this code:
squareIt = Proc.new do |x|
x * x
end
doubleIt = Proc.new do |x|
x + x
end
def compose proc1, proc2
Proc.new do |x|
proc2.call(proc1.call(x))
end
end
doubleThenSquare = compose(doubleIt, squareIt)
squareThenDouble = compose(squareIt, doubleIt)
doubleThenSquare.call(5)
squareThenDouble.call(5)
doubleThenSquare is called with 5. doubleThenSquare is equal to the return value of compose, which has its two parameters doubleIt and squareIt passed.
I don't see how 5 is passed all its way into the different procs Proc.new do |x|. How does it know what x is in each case?
Let's step through it.
doubleIt = Proc.new do |x|
x + x
end
#=> #<Proc:0x00000002326e08#(irb):1429>
squareIt = Proc.new do |x|
x * x
end
#=> #<Proc:0x00000002928bf8#(irb):1433>
proc1 = doubleIt
proc2 = squareIt
compose returns the proc proc3.
proc3 = Proc.new do |x|
proc2.call(proc1.call(x))
end
#=> #<Proc:0x000000028e7608#(irb):1445>
proc3.call(x) is executed in the same way as
proc3_method(x)
where
def proc3_method(x)
y = proc1.call(x)
proc2.call(y)
end
When x = 5,
y = proc1.call(5)
#=> 10
proc2.call(10)
#=> 100
proc3_method(5) therefore returns 100, as does proc3.call(5).
Related
Is there a Ruby method for determining if all instance variables of two instances of the same class are equal?
Is there a Ruby method for comparing two objects based on whether all of their instance variables are equal? The method would behave like this code. class Coordinates attr_reader :x, :y def initialize(x, y) #x = x #y = y end end coordinates1 = Coordinates.new(0, 0) coordinates2 = Coordinates.new(0, 0) coordinates3 = Coordinates.new(1, 0) compare(coordinates1, coordinates1) # => true compare(coordinates1, coordinates2) # => true compare(coordinates1, coordinates3) # => false Does this method or something similar exist?
There is no built-in method for this, but you could quite easily write one. However, I think you're asking an XY question. Here is what I think the question is supposed to say: How should I define a method to check that two Coordinates instances are equal? And here's my answer: Define a custom == method: class Coordinates attr_reader :x, :y def initialize(x, y) #x = x #y = y end def ==(other) return super unless other.is_a?(Coordinates) x == other.x && y == other.y end end ...But in the spirit of StackOverflow, here's some meta-programming to check whether all instance variables have the same name and value: # returns true if all objects have the same instance variable names and values def compare(*objects) objects.map do |object| object.instance_variables.map do |var_name| [var_name, object.instance_variable_get(var_name)] end end.uniq.count == 1 end
Case 1 class A def initialize(x,y) #x = x #y = y end def m #x = 5 #y = 6 end end a1 = A.new(1,2) #=> #<A:0x00005d22a3878048 #x=1, #y=2> a1.m a1 #=> #<A:0x00005d22a3878048 #x=5, #y=6> a2 = A.new(3,4) #=> #<A:0x00005d22a38b5330 #x=3, #y=4> a2.m a2 #=> #<A:0x00005d22a38b5330 #x=5, #y=6> Then, a1.instance_variables.all? { |e| a1.instance_variable_get(e) == a2.instance_variable_get(e) } #=> true tells us that the values of #x and the values of #y are the same for both instances. Case 2 Now let's change the code so that another instance variable is added conditionally. class A def initialize(x,y) #x = x #y = y end def m #z = 3 if #x == 3 #x = 5 #y = 6 end end a1 = A.new(1,2) #=> #<A:0x000057d1fd563c78 #x=1, #y=2> a1.m a1 #=> #<A:0x000057d1fd27f200 #x=5, #y=6> a2 = A.new(3,4) #=> #<A:0x000057d1fd57cb38 #x=3, #y=4> a2.m a2 #=> #<A:0x000057d1fd2f9e10 #x=5, #y=6, #z=3> At this point are all instance variables of one of these instances equal to the corresponding instance variable of the other instance? No, because a2 has an additional instance variable, #z. Therefore, a1.instance_variables.all? { |e| a1.instance_variable_get(e) == a2.instance_variable_get(e) } #=> true gives the wrong answer, for obvious reasons. Perhaps we could test as follows: a1.instance_variables.all? { |e| a1.instance_variable_get(e) == a2.instance_variable_get(e) } && a2.instance_variables.all? { |e| a1.instance_variable_get(e) == a2.instance_variable_get(e) } #=> true && false => false This has a gotcha, however, if #z equals nil. Case 3 class A def initialize(x,y) #x = x #y = y end def m #z = nil if #x == 3 #x = 5 #y = 6 end end a1 = A.new(1,2) #=> #<A:0x000057d1fd2d18e8 #x=1, #y=2> a1.m a1 #=> #<A:0x000057d1fd2d18e8 #x=5, #y=6> a2 = A.new(3,4) #=> #<A:0x000057d1fd46b460 #x=3, #y=4> a2.m a2 #=> #<A:0x000057d1fd46b460 #x=5, #y=6, #z=nil> a1.instance_variables.all? { |e| a1.instance_variable_get(e) == a2.instance_variable_get(e) } && a2.instance_variables.all? { |e| a1.instance_variable_get(e) == a2.instance_variable_get(e) } #=> true && true => true We obtain this incorrect result because: class A end A.new.instance_variable_get(:#z) #=> nil We therefore must confirm that if one instance has an instance variable named e, so does the other instance, and that each pair of instance variables with the same name are equal. One way to do that is as follows: (a1.instance_variables.sort == a2.instance_variables.sort) && a1.instance_variables.all? { |e| a1.instance_variable_get(e) == a2.instance_variable_get(e) } #=> false && true => false See Enumerable#all?, Object#instance_variables and Object#instance_variable_get.
How do I fix a problem to call a function in Ruby?
I'm trying to use some ruby code that I've found in Github. I've downloaded the code and did the necessary imports the "requires" and tried to run it as it is described in the readme file on github repository. The code is the following: In the file pcset_test.rb the code is the following: require './pcset.rb' require 'test/unit' # # When possible, test cases are adapted from # Introduction to Post-Tonal Theory by Joseph N. Straus, # unless obvious or otherwise noted. # class PCSetTest < Test::Unit::TestCase def test_init #assert_raise(ArgumentError) {PCSet.new []} assert_raise(ArgumentError) {PCSet.new [1, 2, 3, 'string']} assert_raise(ArgumentError) {PCSet.new "string"} assert_raise(ArgumentError) {PCSet.new [1, 2, 3.6, 4]} assert_equal([0, 1, 2, 9], PCSet.new([0, 1, 2, 33, 13]).pitches) assert_equal([3, 2, 1, 11, 10, 0], PCSet.new_from_string('321bac').pitches) assert_equal([0,2,4,5,7,11,9], PCSet.new([12,2,4,5,7,11,9]).pitches) assert_nothing_raised() {PCSet.new []} end def test_inversion end def test_transposition end def test_multiplication end # # set normal prime forte # # 0,2,4,7,8,11 7,8,11,0,2,4 0,1,4,5,7,9 6-31 # 0,1,2,4,5,7,11 11,0,1,2,4,5,7 0,1,2,3,5,6,8 7-Z36 # 0,1,3,5,6,7,9,10,11 5,6,7,9,10,11,0,1,3 0,1,2,3,4,6,7,8,10 9-8 # def test_normal_form testPC = PCSet.new [0,4,8,9,11] assert_kind_of(PCSet, testPC.normal_form) assert_equal([8,9,11,0,4], testPC.normal_form.pitches) assert_equal([10,1,4,6], PCSet.new([1,6,4,10]).normal_form.pitches) assert_equal([2,4,8,10], PCSet.new([10,8,4,2]).normal_form.pitches) assert_equal([7,8,11,0,2,4], PCSet.new([0,2,4,7,8,11]).normal_form.pitches) assert_equal([11,0,1,2,4,5,7], PCSet.new([0,1,2,4,5,7,11]).normal_form.pitches) assert_equal([5,6,7,9,10,11,0,1,3], PCSet.new([0,1,3,5,6,7,9,10,11]).normal_form.pitches) end def test_prime_form assert_equal([0,1,2,6], PCSet.new([5,6,1,7]).prime.pitches) assert_equal([0,1,4], PCSet.new([2,5,6]).prime.pitches) assert_equal([0,1,4,5,7,9], PCSet.new([0,2,4,7,8,11]).prime.pitches) assert_equal([0,1,2,3,5,6,8], PCSet.new([0,1,2,4,5,7,11]).prime.pitches) assert_equal([0,1,2,3,4,6,7,8,10], PCSet.new([0,1,3,5,6,7,9,10,11]).prime.pitches) end def test_set_class testPcs = PCSet.new([2,5,6]) testPrime = testPcs.prime assert_equal([ [2,5,6], [3,6,7], [4,7,8], [5,8,9], [6,9,10], [7,10,11], [8,11,0],[9,0,1], [10,1,2],[11,2,3],[0,3,4], [1,4,5], [6,7,10],[7,8,11],[8,9,0], [9,10,1],[10,11,2],[11,0,3], [0,1,4], [1,2,5], [2,3,6], [3,4,7], [4,5,8], [5,6,9] ].sort, PCSet.new([2,5,6]).set_class.map{|x| x.pitches}) assert_equal(testPcs.set_class.map{|x| x.pitches}, testPrime.set_class.map{|x| x.pitches}) end def test_interval_vector assert_equal([2,1,2,1,0,0], PCSet.new([0,1,3,4]).interval_vector) assert_equal([2,5,4,3,6,1], PCSet.new([0,1,3,5,6,8,10]).interval_vector) assert_equal([0,6,0,6,0,3], PCSet.new([0,2,4,6,8,10]).interval_vector) end def test_complement assert_equal([6,7,8,9,10,11], PCSet.new([0,1,2,3,4,5]).complement.pitches) assert_equal([3,4,5], PCSet.new([0,1,2], 6).complement.pitches) end # # Test values from (Morris 1991), pages 105-111 # Citation: # Morris. Class Notes for Atonal Music Theory # Lebanon, NH. Frog Peak Music, 1991. # def test_invariance_vector assert_equal([1,0,0,0,5,6,5,5],PCSet.new([0,2,5]).invariance_vector) assert_equal([2,2,2,2,6,6,6,6],PCSet.new([0,1,6,7]).invariance_vector) assert_equal([6,6,6,6,6,6,6,6],PCSet.new([0,2,4,6,8,10]).invariance_vector) assert_equal([1,0,0,0,0,0,0,0],PCSet.new([0,1,2,3,4,5,8]).invariance_vector) assert_equal([1,0,0,1,0,0,0,0],PCSet.new([0,1,2,3,5,6,8]).invariance_vector) assert_equal([12,12,12,12,0,0,0,0],PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).invariance_vector) end # # Test values from (Huron 1994). Huron rounds, thus the 0.01 margin of error. # Citation: # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: # Common Scales Exhibit Optimum Tonal Consonance. # Music Perception (1994) vol. 11 (3) pp. 289-305 # def test_huron h1 = PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).huron assert_in_delta(-0.2, h1[0], 0.01) assert_in_delta(0.21, h1[1], 0.01) h2 = PCSet.new([0,2,4,5,7,9,11]).huron assert_in_delta(4.76, h2[0], 0.01) assert_in_delta(0.62, h2[1], 0.01) end def test_coherence end end And in the file pcset.rb the folloing code: # # => PCSet Class for Ruby # => Beau Sievers # => Hanover, Fall 2008. # # # TODO: Make this a module to avoid namespace collisions. # Lilypond and MusicXML output # include Math def choose(n, k) return [[]] if n.nil? || n.empty? && k == 0 return [] if n.nil? || n.empty? && k > 0 return [[]] if n.size > 0 && k == 0 c2 = n.clone c2.pop new_element = n.clone.pop choose(c2, k) + append_all(choose(c2, k-1), new_element) end def append_all(lists, element) lists.map { |l| l << element } end def array_to_binary(array) array.inject(0) {|sum, n| sum + 2**n} end # the following method is horrifically inelegant # but avoids monkey-patching. # TODO: do this right, incl. error checking def pearsons(x, y) if !x.is_a?(Array) || !y.is_a?(Array) then raise StandardError, "x and y must be arrays", caller end if x.size != y.size then raise StandardError, "x and y must be same size", caller end sum_x = x.inject(0) {|sum, n| sum + n} sum_y = y.inject(0) {|sum, n| sum + n} sum_square_x = x.inject(0) {|sum, n| sum + n * n} sum_square_y = y.inject(0) {|sum, n| sum + n * n} xy = [] x.zip(y) {|a, b| xy.push(a * b)} sum_xy = xy.inject(0) {|sum, n| sum + n} num = sum_xy - ((sum_x * sum_y)/x.size) den = Math.sqrt((sum_square_x - ((sum_x*sum_x)/x.size)) * (sum_square_y - ((sum_y*sum_y)/x.size))) (num/den) end class PCSet include Comparable attr_reader :pitches, :base, :input def initialize(pcarray, base = 12) if pcarray.instance_of?(Array) && pcarray.all?{|pc| pc.instance_of?(Fixnum)} #base, #input = base, pcarray #pitches = pcarray.map{ |x| x % #base }.uniq else raise ArgumentError, "Improperly formatted PC array", caller end end def PCSet.new_from_string(pcstring, base = 12) if base > 36 then raise StandardError, "Use PCSet.new to create pcsets with a base larger than 36", caller end pcarray = [] pcstring.downcase.split(//).each do |c| if c <= 'z' and c >= '0' then pcarray.push(c.to_i(36)) end end PCSet.new pcarray, base end def <=>(pcs) #pitches <=> pcs.pitches end def [](index) #pitches[index] end # Intersection def &(other) PCSet.new #pitches & other.pitches end # Union def |(other) PCSet.new #pitches | other.pitches end def inspect #pitches.inspect end def length #pitches.length end def invert(axis = 0) PCSet.new #pitches.map {|x| (axis-x) % #base} end def invert!(axis = 0) #pitches.map! {|x| (axis-x) % #base} end def transpose(interval) PCSet.new #pitches.map {|x| (x + interval) % #base} end def transpose!(interval) #pitches.map! {|x| (x + interval) % #base} end def multiply(m = 5) PCSet.new #pitches.map {|x| (x * m) % #base} end def multiply!(m = 5) #pitches.map! {|x| (x * m) % #base} end def zero transpose(-1 * #pitches[0]) end def zero! transpose!(-1 * #pitches[0]) end def transpositions (0..(#base-1)).to_a.map{|x| #pitches.map {|y| (y + x) % #base}}.sort.map {|x| PCSet.new x} end def transpositions_and_inversions(axis = 0) transpositions + invert(axis).transpositions end # # Normal form after Straus. Morris and AthenaCL do this differently. # def normal_form tempar = #pitches.sort arar = [] # [[1,4,7,8,10],[4,7,8,10,1], etc.] get each cyclic variation tempar.each {arar.push PCSet.new(tempar.unshift(tempar.pop))} most_left_compact(arar) end def normal_form! #pitches = normal_form.pitches end def is_normal_form? self.pitches == self.normal_form.pitches end def set_class transpositions_and_inversions.map{|pcs| pcs.normal_form}.sort end def prime most_left_compact([normal_form.zero, invert.normal_form.zero]) end def prime! self.pitches = self.prime.pitches end def is_prime? self.pitches == self.prime.pitches end def complement new_pitches = [] #base.times do |p| if !#pitches.include? p then new_pitches.push p end end PCSet.new new_pitches end def full_interval_vector pairs = choose(#pitches, 2) # choose every pc pair intervals = pairs.map {|x| (x[1] - x[0]) % #base} # calculate every interval i_vector = Array.new(#base-1).fill(0) intervals.each {|x| i_vector[x-1] += 1} # count the intervals i_vector end def interval_vector i_vector = full_interval_vector (0..((#base-1)/2)-1).each {|x| i_vector[x] += i_vector.pop} i_vector end # # Morris's invariance vector # def invariance_vector(m = 5) t = transpositions.map!{|pcs| self & pcs} ti = invert.transpositions.map!{|pcs| self & pcs} tm = multiply(m).transpositions.map!{|pcs| self & pcs} tmi = invert.multiply(m).transpositions.map!{|pcs| self & pcs} tc = complement.transpositions.map!{|pcs| self & pcs} tic = complement.invert.transpositions.map!{|pcs| self & pcs} tmc = complement.multiply(m).transpositions.map!{|pcs| self & pcs} tmic = complement.invert.multiply(m).transpositions.map!{|pcs| self & pcs} [t, ti, tm, tmi, tc, tic, tmc, tmic].map{|x| x.reject{|pcs| pcs.pitches != #pitches}.length} end # Huron's aggregate dyadic consonance measure. # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: # Common Scales Exhibit Optimum Tonal Consonance. # Music Perception (1994) vol. 11 (3) pp. 289-305 def huron if #base != 12 then raise StandardError, "PCSet.huron only makes sense for mod 12 pcsets", caller end # m2/M7 M2/m7 m3/M6 M3/m6 P4/P5 A4/d5 huron_table = [-1.428, -0.582, 0.594, 0.386, 1.240, -0.453] interval_consonance = [] interval_vector.zip(huron_table) {|x, y| interval_consonance.push(x * y) } aggregate_dyadic_consonance = interval_consonance.inject {|sum, n| sum + n} [aggregate_dyadic_consonance, pearsons(interval_vector, huron_table)] end # # Balzano's vector of relations. Citation for all Balzano methods: # # Balzano. "The Pitch Set as a Level of Description for Studying Musical # Pitch Perception" in Music, Mind, and Brain ed. Clynes. Plenum Press. 1982. # def vector_of_relations (0..length-1).to_a.map do |i| (0..length-1).to_a.map do |j| (#pitches[(i + j) % length] - #pitches[i]) % #base end end end # # Checks if the set satisfies Balzano's uniqueness. # def is_unique? vector_of_relations.uniq.size == vector_of_relations.size end # # Checks if the set satisfies Balzano's scalestep-semitone coherence. # For all s[i] and s[i1]: # j < k => v[i][j] < v[i1][k] # Where j and k are scalestep-counting indices. # And unless v[i][j] == 6 (a tritone), in which case the strict inequality is relaxed. # def is_coherent? v = vector_of_relations truth_array = [] all_pair_indices = choose((0..length-1).to_a, 2) all_pair_indices.each do |i, i1| all_pair_indices.each do |j, k| if v[i][j] == 6 truth_array.push(v[i][j] <= v[i1][k]) else truth_array.push(v[i][j] < v[i1][k]) end if v[i1][j] == 6 truth_array.push(v[i1][j] <= v[i][k]) else truth_array.push(v[i1][j] < v[i][k]) end end end !truth_array.include?(false) end # # Strict Balzano coherence, no inequality relaxation for tritones. # def is_strictly_coherent? v = vector_of_relations truth_array = [] all_pair_indices = choose((0..length-1).to_a, 2) all_pair_indices.each do |i, i1| all_pair_indices.each do |j, k| truth_array.push(v[i][j] < v[i1][k]) truth_array.push(v[i1][j] < v[i][k]) end end !truth_array.include?(false) end def notes(middle_c = 0) noteArray = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'] if #base != 12 then raise StandardError, "PCSet.notes only makes sense for mod 12 pcsets", caller end out_string = String.new transpose(-middle_c).pitches.each do |p| out_string += noteArray[p] + ", " end out_string.chop.chop end def info print "modulo: #{#base}\n" print "raw input: #{#input.inspect}\n" print "pitch set: #{#pitches.inspect}\n" print "notes: #{notes}\n" print "normal: #{normal_form.inspect}\n" print "prime: #{prime.inspect}\n" print "interval vector: #{interval_vector.inspect}\n" print "invariance vector: #{invariance_vector.inspect}\n" print "huron ADC: #{huron[0]} pearsons: #{huron[1]}\n" print "balzano coherence: " if is_strictly_coherent? print "strictly coherent\n" elsif is_coherent? print "coherent\n" else print "false\n" end end # def lilypond # # end # # def musicXML # # end ############################################################################### private # # Convert every pitch array to a binary representation, e.g.: # [0,2,4,8,10] -> 010100010101 # 2^n: BA9876543210 # The smallest binary number is the most left-compact. # def most_left_compact(pcset_array) if !pcset_array.all? {|pcs| pcs.length == pcset_array[0].length} raise ArgumentError, "PCSet.most_left_compact: All PCSets must be of same cardinality", caller end zeroed_pitch_arrays = pcset_array.map {|pcs| pcs.zero.pitches} binaries = zeroed_pitch_arrays.map {|array| array_to_binary(array)} winners = [] binaries.each_with_index do |num, i| if num == binaries.min then winners.push(pcset_array[i]) end end winners.sort[0] end end I'm calling them as follows: > my_pcset = PCSet.new([0,2,4,6,8,10]) > my_pcset2 = PCSet.new([1,5,9]) It shoud return: > my_pcset = PCSet.new([0,2,4,6,8,10]) => [0, 2, 4, 6, 8, 10] > my_pcset2 = PCSet.new([1,5,9]) => [1, 5, 9] But is returning nothing. The code is available on github Thanks
Try this in terminal: irb -r ./path_to_directory/pcset.rb and then initialize the objects.
I think the documentation for the repo is bad as it does not explain how you should be running this. The result of my_pcset = PCSet.new([0,2,4,6,8,10]) should set my_pcset to an instance of a PCSet not an array, so these lines from the README file are confusing at best. 3. How to use it Make new PCSets: my_pcset = PCSet.new([0,2,4,6,8,10]) => [0, 2, 4, 6, 8, 10] my_pcset2 = PCSet.new([1,5,9]) => [1, 5, 9] Looking at the code, I see inspect has been delegated to #pitches def inspect #pitches.inspect end I think if you inspect my_pcset you will get the expected result. my_pcset = PCSet.new([0,2,4,6,8,10]) p my_pcset # will print [0, 2, 4, 6, 8, 10] or `my_pcset.inspect` will return what you are expecting.
Turning a method into an enumerable method
I rewrote the map method: def my_map(input, &block) mod_input = [] x = -1 while x < input.length - 1 x = x + 1 if block == nil return input break end mod_input.push(block.call(input[x])) end return mod_input end I need to call this code as I would call map or reverse. Does anyone know the syntax for that?
Are you asking how you put a method into a module? That's trivial: module Enumerable def my_map(&block) mod_input = [] x = -1 while x < length - 1 x = x + 1 if block == nil return self break end mod_input.push(block.call(self[x])) end return mod_input end end [1, 2, 3, 4, 5].my_map(&2.method(:*)) # => [2, 4, 6, 8, 10] Or are you asking how to make your method an Enumerable method? That's more involved: your method currently uses many methods that are not part of the Enumerable API. So, even if you make it a member of the Enumerable module, it won't be an Enumerable method. Enumerable methods can only use each or other Enumerable methods. You use length and [] both of which are not part of the Enumerable interface, for example, Set doesn't respond to []. This would be a possible implementation, using the Enumerable#inject method: module Enumerable def my_map return enum_for(__method__) unless block_given? inject([]) {|res, el| res << yield(el) } end end [1, 2, 3, 4, 5].my_map(&2.method(:*)) # => [2, 4, 6, 8, 10] A less elegant implementation using each module Enumerable def my_map return enum_for(__method__) unless block_given? [].tap {|res| each {|el| res << yield(el) }} end end [1, 2, 3, 4, 5].my_map(&2.method(:*)) # => [2, 4, 6, 8, 10] Note that apart from being simply wrong, your code is very un-idiomatic. There is also dead code in there. the break is dead code: the method returns in the line just before it, therefore the break will never be executed. You can just get rid of it. def my_map(&block) mod_input = [] x = -1 while x < length - 1 x = x + 1 if block == nil return self end mod_input.push(block.call(self[x])) end return mod_input end Now that we have gotten rid of the break, we can convert the conditional into a guard-style statement modifier conditional. def my_map(&block) mod_input = [] x = -1 while x < length - 1 x = x + 1 return self if block == nil mod_input.push(block.call(self[x])) end return mod_input end It also doesn't make sense that it is in the middle of the loop. It should be at the beginning of the method. def my_map(&block) return self if block == nil mod_input = [] x = -1 while x < length - 1 x = x + 1 mod_input.push(block.call(self[x])) end return mod_input end Instead of comparing an object against nil, you should just ask it whether it is nil?: block.nil? def my_map(&block) return self if block.nil? mod_input = [] x = -1 while x < length - 1 x = x + 1 mod_input.push(block.call(self[x])) end return mod_input end Ruby is an expression-oriented language, the value of the last expression that is evaluated in a method body is the return value of that method body, there is no need for an explicit return. def my_map(&block) return self if block.nil? mod_input = [] x = -1 while x < length - 1 x = x + 1 mod_input.push(block.call(self[x])) end mod_input end x = x + 1 is more idiomatically written x += 1. def my_map(&block) return self if block.nil? mod_input = [] x = -1 while x < length - 1 x += 1 mod_input.push(block.call(self[x])) end mod_input end Instead of Array#push with a single argument it is more idiomatic to use Array#<<. def my_map(&block) return self if block.nil? mod_input = [] x = -1 while x < length - 1 x += 1 mod_input << block.call(self[x]) end mod_input end Instead of Proc#call, you can use the .() syntactic sugar. def my_map(&block) return self if block.nil? mod_input = [] x = -1 while x < length - 1 x += 1 mod_input << block.(self[x]) end mod_input end If you don't want to store, pass on or otherwise manipulate the block as an object, there is no need to capture it as a Proc. Just use block_given? and yield instead. def my_map return self unless block_given? mod_input = [] x = -1 while x < length - 1 x += 1 mod_input << yield(self[x]) end mod_input end This one is opinionated. You could move incrementing the counter into the condition. def my_map return self unless block_given? mod_input = [] x = -1 while (x += 1) < length mod_input << yield(self[x]) end mod_input end And then use the statement modifier form. def my_map return self unless block_given? mod_input = [] x = -1 mod_input << yield(self[x]) while (x += 1) < length mod_input end Also, your variable names could use some improvements. For example, what does mod_input even mean? All I can see is that it is what you output, so why does it even have "input" in its name? And what is x? def my_map return self unless block_given? result = [] index = -1 result << yield(self[index]) while (index += 1) < length result end This whole sequence of initializing a variable, then mutating the object assigned to that variable and lastly returning the object can be simplified by using the K Combinator, which is available in Ruby as Object#tap. def my_map return self unless block_given? [].tap {|result| index = -1 result << yield(self[index]) while (index += 1) < length } end The entire while loop is useless. It's just re-implementing Array#each, which is a) unnecessary because Array#each already exists, and b) means that your my_map method will only work with Arrays but not other Enumerables (for example Set or Enumerator). So, let's just use each instead. def my_map return self unless block_given? [].tap {|result| each {|element| result << yield(element) } } end Now it starts to look like Ruby code! What you had before was more like BASIC written in Ruby syntax. This pattern of first creating a result object, then modifying that result object based on each element of a collection and in the end returning the result is very common, and it even has a fancy mathematical name: Catamorphism, although in the programming world, we usually call it fold or reduce. In Ruby, it is called Enumerable#inject. def my_map return self unless block_given? inject([]) {|result, element| result << yield(element) } end That return self is strange. map is supposed to return a new object! You don't return a new object, you return the same object. Let's fix that. def my_map return dup unless block_given? inject([]) {|result, element| result << yield(element) } end And actually, map is also supposed to return an Array, but you return whatever it is that you called map on. def my_map return to_a unless block_given? inject([]) {|result, element| result << yield(element) } end But really, if you look at the documentation of Enumerable#map, you will find that it returns an Enumerator and not an Array when called without a block. def my_map return enum_for(:my_map) unless block_given? inject([]) {|result, element| result << yield(element) } end And lastly, we can get rid of the hardcoded method name using the Kernel#__method__ method. def my_map return enum_for(__method__) unless block_given? inject([]) {|result, element| result << yield(element) } end Now, that looks a lot better!
class Array def my_map(&block) # your code, replacing `input` with `self` end end The code itself is not really idiomatic Ruby - while is very rarely used for iteration over collections, and if you don't need to pass a block somewhere else, it is generally cleaner to use block_given? instead of block.nil? (let alone block == nil), and yield input[x] instead of block.call(input[x]).
How to yield 2 blocks in 1 method
How can I yield two diferent blocks in the same method the example code: def by_two(n,a) yield n yield a end proc1 = proc {|x| p x * 2} proc2 = proc {|x| x + 100} by_two(10, 300, &proc1, &proc2) the error is such - main.rb:7: syntax error, unexpected ',', expecting ')' by_two(10, 300, &proc1, &proc2) Any suggestions where and what is wrong? Thanks
Blocks are a lightweight way of passing a single anonymous procedure to a method. So, by definition, there cannot be two blocks passed to a method. It's not just semantically impossible, it isn't even possible syntactically. Ruby does support first-class procedures in the form of Procs, however, and since those are just objects like any other object, you can pass as many of them as you want: def by_two(n, a, proc1, proc2) proc1.(n) proc2.(a) end proc1 = proc {|x| p x * 2} proc2 = proc {|x| x + 100} by_two(10, 300, proc1, proc2) # 20 # => 400 Since the introduction of lambda literals in Ruby 1.9, Procs are almost as syntactically lightweight as blocks, so there is not a big difference anymore: by_two(10, 300, -> x { p x * 2 }, -> x { x + 100 }) # 20 # => 400
You cannot yield two blocks in a method. But you can take two procs. def by_two(n, a, pr1, pr2) pr1.call(n) pr2.call(a) end by_two(10, 300, proc1, proc2)
The answer to your question is: You cannot do it if you insist on blocks! Ruby does not support multiple blocks per method. A way to go around this is to pass two procs as variables like this: def by_two(n,a, proc1=nil, proc2=nil) if proc1 || proc2 proc1.yield n if proc1 puts proc2.yield a if proc2 else puts "no procs" end end proc1 = proc {|x| p x * 2} proc2 = proc {|x| x + 100} by_two(10, 300, proc1, proc2) by_two(10, 300, proc1) by_two(10, 300) Output: 20 400 20 no procs Another possibility is this: NO_OP = proc {} def by_two(n,a, proc1=NO_OP, proc2=NO_OP) if proc1 == NO_OP && proc2 == NO_OP puts "no procs" else proc1.yield n proc2.yield a end end proc1 = proc {|x| p x * 2} proc2 = proc {|x| p x + 100} by_two(10, 300, proc1, proc2) by_two(10, 300, proc1) by_two(10, 300) It has the same output.
Ruby find max number w/o running method twice
I want to find the max number without running the function twice def foo(num) num * 10 end def bar x = 0 for i in 0..5 if foo(i) > x x = foo(i) # I don't want to run foo a second time end end end
How about def bar (1..5).map{|i| foo(i)}.max end This will traverse 1 to 5, and max a new enumerable with foo(i) instead of i, then return the max.
If you want the value of x: define_method(:foo) { |x| x * 10 } (1..5).max_by { |x| foo(x) } #=> 5 If you want the value of f(x): (1..5).map { |x| foo(x) }.max #=> 50
You can save the result of the function as a variable, so you can use it later without calling the function again. Applied to your code example, it would look like this: #... fooOfI = foo(i) if fooOfI > x x = fooOfI end #...
Store the result of the method in a local variable. def bar x = 0 for i in 0..5 foo_result = foo i if foo_result > x x = foo_result end end end
I would do some change in your code : def foo(num) num * 10 end def bar x = 0 for i in 0..5 _,x = [foo(i),x].sort #or you can write as x = [foo(i),x].max end x end p bar # >> 50
Elegant and simple foo = -> x { x * 10 } (1..5).map(&foo).max # => 50 In one iteration (no so elegant but performs better) def foo(num); num * 10; end; (1..5).reduce(-1.0/0) { |a, e| f = foo(e); f > a ? f : a } # => 50