Примеры функционального кода в ruby - PullRequest
4 голосов
/ 30 сентября 2010

Я ищу примеры функционального кода в ruby. Может быть, вы знаете некоторые драгоценные камни, где я могу найти такой код?

Ответы [ 6 ]

7 голосов
/ 17 июня 2011

Я собираю примеры функционального программирования на Ruby:

4 голосов
/ 30 сентября 2010

Пару месяцев назад мне стало интересно, возможно ли сделать миксин, который ведет себя точно как Enumerable, но основан на свёртывании вместо перечисления . Точнее, я уже знал , что это можно было сделать, поскольку fold - это общая итерационная операция, выраженная по степени выраженности foreach , но Я хотел знать, как это будет выглядеть и как тяжело это будет.

Мне стало скучно после того, как я реализовал большинство методов вплоть до буквы g , так что это неполно. Кроме того, версия, которую я показываю здесь, упрощена, поскольку заставить ее вести себя точно подобно Enumerable - это PITA в Ruby. (Например, там есть перегруженные методы, но Ruby не поддерживает перегрузку. Это не проблема для большинства реализаций Ruby, потому что они реализуют Enumerable в Java или C # или других языках, которые делают поддерживает перегрузку, но это довольно болезненно, когда вы делаете это в чистом Ruby, поэтому я решил оставить это без внимания.)

Enumerable полон процедур высшего порядка, и, конечно, fold (или reduce / inject, как его называют в Ruby) - это само по себе выше порядок, так что этот код полон их.

module Enumerable
  def all?
    reduce(true) {|res, el| res && yield(el) }
  end

  def any?
    reduce(false) {|res, el| res || yield(el) }
  end

  def collect
    reduce([]) {|res, el| res + yield(el) }
  end
  alias_method :map, :collect

  def count
    reduce(0) {|res, el| res + 1 if yield el }
  end

  def detect
    reduce(nil) {|res, el| if yield el then el end unless res }
  end
  alias_method :find, :detect

  def drop(n=1)
    reduce([]) {|res, el| res.tap {|res| res + el unless n -= 1 >= 0 }}
  end

  def each
    reduce(nil) {|_, el| yield el }
  end

  def each_with_index
    reduce(-1) {|i, el| (i+1).tap {|i| yield el, i }}
  end

  def find_all
    reduce([]) {|res, el| res.tap {|res| res + el if yield el }}
  end
  alias_method :select, :find_all

  def grep(pattern)
    reduce([]) {|res, el| res.tap {|res| res + yield(el) if pattern === el }}
  end

  def group_by
    reduce(Hash.new {|hsh, key| hsh[key] = [] }) {|res, el| res.tap {|res|
        res[yield el] << el
    }}
  end

  def include?(obj)
    reduce(false) {|res, el| break true if res || el == obj }
  end

  def reject
    reduce([]) {|res, el| res.tap {|res| res + el unless yield el }}
  end
end

Вы заметите, что я использую побочные эффекты в некоторых местах. Я мог бы написать это без побочных эффектов, но это было бы намного сложнее. И проблема в том, что я не могу гарантировать, что метод reduce, который предоставляется клиентом миксина или любым из методов из базовой библиотеки, не имеет побочные эффекты.

Другим хорошим примером является библиотека Prelude для Ruby, которая представляет собой реализацию некоторых частей Haskell Prelude (эквивалент Haskell для базовой библиотеки Ruby) в Ruby.

4 голосов
/ 30 сентября 2010

Вы смотрели на Enumerable?

(1..100).select { |i| i % 5 == 0 }.map { |i| i * 5 }.take(3) #=> [25, 50, 75]
2 голосов
/ 30 сентября 2010

Возможно, вас заинтересует доклад Дина Уэмплера «Улучшить Ruby через функциональное программирование», в котором показано, как использовать стиль FP с Ruby:

http://vimeo.com/6701329

Также есть «Функциональное мышление в Ruby "Тома Стюарта:

http://skillsmatter.com/podcast/ajax-ria/enumerators

1 голос
/ 01 октября 2010

"Язык программирования Ruby" имеет половину главы, в которой они работают над парой дополнений к перечисляемому, чтобы рубин выглядел как Хаскелл.

Вы также можете просмотреть предыдущие вопросы переполнения стека, помеченные как "Ruby", так и "функциональное программирование" здесь .

0 голосов
/ 17 декабря 2013

Это не красивый и не эффективный код.Это объясняет разницу между функциональным и нефункциональным стилем.

При работе с функциональными концепциями в ruby ​​нужно подумать о том, что наш vm не создан для него, поэтому без прозрачной ссылочной оптимизации выесть больше памяти, пишущей в функциональном стиле.

# non functional
class Post
  attr_accessor :title, :body
end

all_posts = []

# create a post
p = Post.new
p.title = "Hello world"
p.body  = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

# put post in the array
all_posts << p

# fix post title
all_posts[0].title = "Opps, fixed title"

#functional
class Post
  attr_reader :title, :body

  def initialize(attrs={})
    attrs.each {|k,v| instance_variable_set "@#{k.to_s}", v }
  end
end

all_posts = []

# create a post
p = Post.new({title: "Hello world", body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."})
# add to array via new array creation
all_posts = all_posts + [p] # be wary when all_posts is a very large collection!

old_post = p
p = Post.new({title: "Oops, fixed title", body: old_post.body})
all_posts = all_posts - [old_post] + [p]

Я часто пишу о функциональном программировании в ruby ​​в моем блоге: http://rubylove.io

...