передача функций в качестве аргументов в ruby - PullRequest
5 голосов
/ 14 января 2009

Я пытаюсь сосредоточиться на функциональном программировании на ruby, и, похоже, нет хорошей документации.

По сути, я пытаюсь написать функцию объединения, которая будет иметь сигнатуру типа Haskell:

[a] -> [a] -> (a -> a -> a) -> [a]

Итак

combine([1,2,3], [2,3,4], plus_func) => [3,5,7]
combine([1,2,3], [2,3,4], multiply_func) => [2,6,12]

и т.д.

Я нашел кое-что об использовании zip и карты, но это ужасно неудобно.

Какой самый "рубиновый" способ реализовать что-то подобное?

Ответы [ 4 ]

10 голосов
/ 14 января 2009

Ну, вы сказали, что знаете о zip и map, так что это, вероятно, бесполезно. Но я выложу на всякий случай.

def combine a, b
    a.zip(b).map { |i| yield i[0], i[1] }
end

puts combine([1,2,3], [2,3,4]) { |i, j| i+j }

Нет, я тоже не нахожу это красивым.

edit - # ruby-lang @ irc.freenode.net предлагает следующее:

def combine(a, b, &block)
    a.zip(b).map(&block)
end

или это, если вы хотите переслать аргументы:

def combine(a, b, *args, &block)
    a.zip(b, *args).map(&block)
end
3 голосов
/ 14 января 2009

Очень наивный подход:

def combine(a1, a2)
  i = 0
  result = []
  while a1[i] && a2[i]
    result << yield(a1[i], a2[i])
    i+=1
  end
  result
end

sum = combine([1,2,3], [2,3,4]) {|x,y| x+y}
prod = combine([1,2,3], [2,3,4]) {|x,y| x*y}

p sum, prod

=>
[3, 5, 7]
[2, 6, 12]

И с произвольными параметрами:

def combine(*args)
  i = 0
  result = []
  while args.all?{|a| a[i]}
    result << yield(*(args.map{|a| a[i]}))
    i+=1
  end
  result
end

РЕДАКТИРОВАТЬ: Я проголосовал за решение zip / map, но вот небольшое улучшение, что в этом плохого?

def combine(*args)
  args.first.zip(*args[1..-1]).map {|a| yield a}
end

sum = combine([1,2,3], [2,3,4], [3,4,5]) {|ary| ary.inject{|t,v| t+=v}}
prod = combine([1,2,3], [2,3,4], [3,4,5]) {|ary| ary.inject(1){|t,v| t*=v}}
p sum, prod
1 голос
/ 17 января 2009

Вы говорите так, как если бы вы также хотели Symbol.to_proc (код Raganwald )

class Symbol
  # Turns the symbol into a simple proc, which is especially useful for enumerations. 
  def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end
end

Теперь вы можете сделать:

(1..100).inject(&:+)

Отказ от ответственности: я не Rubyist. Мне просто нравится функциональное программирование. Так что, скорее всего, это не похоже на Ruby.

0 голосов
/ 14 января 2009

Вы можете передать имя метода в виде символа и использовать Object#send (или Object#__send__) для вызова его по имени. (На самом деле у Ruby нет функций, у него есть методы.)

Вы можете передать lambda или блок, который вызывает желаемый метод для желаемых аргументов. Передача блоков, вероятно, является предпочтительным способом Ruby, когда он работает (т.е. когда у вас есть только один блок для передачи).

Вы извлекаете Method объектов напрямую через Object#method, а затем передаете их и call их, но у меня мало опыта, как это сделать, и я не видел, чтобы это было сделано на практике.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...