2009-07-07 12 views

Répondre

5

est donc ici comment faire taitement avec des blocs, plutôt que des méthodes:

def curry(&block) 
    arity = (block.arity >= 0) ? block.arity : -(block.arity + 1) 
    # return an immediate value if the block has one 
    return block[] if arity == 0 

    # otherwise, curry it argument by argument 
    args = [] 
    innermost = lambda do |last,*extra| 
    args[arity-1] = last 
    block[*(args+extra)] 
    end 
    (0...(arity-1)).to_a.reverse.inject(innermost) do |inner,i| 
    lambda do |arg_i,*extra| 
     args[i] = arg_i 
     # pass extra arguments on to inner calls 
     if extra.empty? 
     inner 
     else 
     inner[*extra] 
     end 
    end 
    end 
end 

et il fonctionne assez bien dans la pratique. Les arguments peuvent être cari ou non, et arguments supplémentaires sont collectés comme d'habitude:

irb> (curry { |x,y| x + y })[1,2] 
#=> 3 
irb> (curry { |x,y| x + y })[1][2] 
#=> 3 
irb> (curry { |x,*ys| ys << x })[1] 
#=> [1] 
irb> (curry { |x,*ys| ys << x })[1,2,3] 
#=> [2, 3, 1] 
irb> (curry { |x,y,*zs| zs << (x+y) })[1,2] 
#=> [3] 
irb> (curry { |x,y,*zs| zs << (x+y) })[1,2,4] 
#=> [4, 3] 
irb> (curry { |x,y,*zs| zs << (x+y) })[1][2] 
#=> [3] 
irb> (curry { |x,y,*zs| zs << (x+y) })[1][2,4] 
#=> [4, 3] 
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1,2,3,4,5] 
#=> 15 
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1][2][3][4][5] 
#=> 15 
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1,2][3][4][5] 
#=> 15 
irb> (curry { |a,b,c,d,e| a+b+c+d+e })[1][2,3,4][5] 
#=> 15 

je pris la décision de conception de ne pas avoir-arg blocs retournent une valeur immédiate sur Currying:

irb> curry { 3 } 
#=> 3 
irb> curry { |*xs| xs } 
#=> [] 

C'est nécessaire pour éviter d'avoir à finir avec curry [] à chaque fois (et est assez similaire à Haskell).

Questions connexes