Передача функций в качестве аргументов в ruby

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

Каким будет наиболее «рубиновый» способ реализации чего-то подобного?

Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
5
0
1 793
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

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} быстрее записывается (и, возможно, более читается). Но вам по-прежнему не хватает комбинационной и коллекционной частей.

krusty.ar 17.01.2009 02:19

Другие вопросы по теме