2009-07-07 20 views

risposta

5

Quindi, ecco come fare currying con blocchi, piuttosto che metodi:

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 

e funziona abbastanza bene nella pratica. Gli argomenti possono essere al curry o no, e argomenti extra vengono raccolti come al solito:

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 

ho preso la decisione di progettazione di avere no-arg blocchi restituiscono un valore immediato sulla currying:

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

Questo è necessario evitare di dover terminare ogni volta con un [] (ed è abbastanza simile a Haskell).

Problemi correlati