Я пытаюсь осмыслить функциональное программирование на 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 и map, но это действительно ужасно в использовании.
Каким будет наиболее «рубиновый» способ реализации чего-то подобного?

Очень наивный подход:
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
Ну, вы сказали, что знаете о 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 }
Нет, я тоже не считаю это красивым.
редактировать - # 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
Вы можете передать имя метода в виде символа и использовать Object#send (или Object#__send__) для вызова его по имени. (У Ruby на самом деле нет функций, у него есть методы.)
Вы можете передать lambda или блок, который вызывает желаемый метод для желаемых аргументов. Передача блоков, вероятно, является предпочтительным способом Ruby, когда он работает (т.е. когда вам нужно передать только один блок).
Вы извлекаете объекты Method напрямую через Object#method, а затем передаете их и call, но у меня мало опыта в этом, и я не видел, чтобы это делалось на практике.
Похоже, вам также может понадобиться Symbol.to_proc (код Раганвальд)
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(&:+)
Отказ от ответственности: я не рубист. Мне просто нравится функциональное программирование. Так что это, скорее всего, будет не похоже на Ruby.
Ваш код правильный, но он просто включает эту часть: {| x, y | x + y} быстрее записывается (и, возможно, более читается). Но вам по-прежнему не хватает комбинационной и коллекционной частей.