`
dcaoyuan
  • 浏览: 306328 次
社区版块
存档分类
最新评论

Ruby函数式风格编程

阅读更多
虽然打算把自己的一个开源项目移植到erlang,但手头的一个工作项目却是在用ruby on rails,花了一段时间了解ruby后,发现ruby还是有点意思的。(反倒是不太喜欢Rails。我喜欢直来直去的编程,rails里面太多的magic,实在让我这个喜欢刨根究底的人难受)。

回到主题,Ruby之所以比Python、groovy和Javascript更适合函数风格的编程,尤其是erlang风格,主要因为以下几点:
  • everything is message(也即任何语句都会返回值)
  • may return multiple values(可以返回多值)
  • parallel assignment(并行赋值)
让我们看几个例子:

例一、
命令式风格:
ruby 代码
 
  1. cond = {}  
  2. if par[:id]  
  3.   feed = Feed.find(par[:id])  
  4.   if feed  
  5.     cond[:feed] = feed.id  
  6.   end  
  7. end  
  8. if par[:m]
  9.   limit = par[:m].to_i  
  10. else  
  11.   limit = 20  
  12. end  
  13. if limit >= 4096  
  14.   limit = 4096  
  15. end  
  16. cond[:limit] = limit  
  17. if par[:d]  
  18.   days = par[:d].to_f  
  19.   if days <= 0 || days >= 365  
  20.     days = 365  
  21.   end  
  22.   cond[:time] = Time.now - days*86400  
  23. end  
函数时风格:
ruby 代码
 
  1. cond = {  
  2.   :feed   => if par[:id]  
  3.                feed = Feed.find(par[:id])  
  4.                feed ? feed.id : nil  
  5.              end,  
  6.   :limit  => begin  
  7.                 limit = par[:m] ? par[:m].to_i : 20  
  8.                 limit >= 4096 ? 4096 : limit  
  9.              end,  
  10.   :time   => if par[:d]  
  11.                days = par[:d].to_f  
  12.                days = days <= 0 || days >= 365 ? 365 : days  
  13.                Time.now - days * 86400  
  14.              end,  
  15. }.delete_if { |k, v| v.nil? } # delete all nil elements of cond  

注意15行删nil项,因为在一个if .. end子句中,总会返回一个值,在不满足所有条件的情况下,就是返回nil

例二
命令式风格:
ruby 代码
 
  1. if f[:mode] == "rss"  
  2.   rss = f[:feed]  
  3.   params[:feed][:channel] = rss.channel.title  
  4.   params[:feed][:description] = rss.channel.description  
  5.   params[:feed][:link] = rss.channel.link  
  6.   params[:feed][:copyright] = rss.channel.copyright  
  7. else  
  8.   atom = f[:feed]  
  9.   params[:feed][:channel] = atom.title  
  10.   params[:feed][:description] = atom.subtitle  
  11.   params[:feed][:link] = atom.links.join  
  12.   params[:feed][:copyright] = atom.rights  
  13. end  
函数式风格:
ruby 代码
 
  1. params[:feed][:channel],  
  2. params[:feed][:description],  
  3. params[:feed][:link],  
  4. params[:feed][:copyright] = if f[:mode] == "rss"  
  5.                               rss = f[:feed]  
  6.                               a, b, c, d = rss.channel.title,  
  7.                                            rss.channel.description,  
  8.                                            rss.channel.link,  
  9.                                            rss.channel.copyright  
  10.                             else  
  11.                               atom = f[:feed]  
  12.                               a, b, c, d = atom.title,  
  13.                                            atom.subtitle,  
  14.                                            atom.links.join,  
  15.                                            atom.rights  
  16.                             end  

或者:
ruby 代码
 
  1. params[:feed][:channel],    
  2. params[:feed][:description],    
  3. params[:feed][:link],    
  4. params[:feed][:copyright] = if f[:mode] == "rss"    
  5.                               rss = f[:feed]   
  6.   
  7.                               [rss.channel.title,    
  8.                                rss.channel.description,    
  9.                                rss.channel.link,    
  10.                                rss.channel.copyright]  
  11.                             else    
  12.                               atom = f[:feed]  
  13.     
  14.                               [atom.title,    
  15.                                atom.subtitle,    
  16.                                atom.links.join,    
  17.                                atom.rights]    
  18.                             end    

函数式风格的好处是,如果这一段代码只是为了给某几个变量赋值,那么函数式的写法一目了然,副作用也变得尽在掌控中。而命令式风格则没那个好处了。


说明:
1、并行赋值的重要性主要在于让处理分支中的多变量赋值时方便,否则例二中的情况就很难用函数式风格处理了
2、例二中的a,b,c,d当然可以直接用[]括起来处理(如上),但我用abcd也是为了让代码一目了然,个人感觉而已。
分享到:
评论
11 楼 cookoo 2007-02-19  
ruby是有很多fp的特性,不过要注意一点:ruby是没有尾递归优化的,所以能用循环或map,filter之类的就用它们,尽量避免用递归。
10 楼 dcaoyuan 2007-02-14  
对,真实的代码不是这样子,是用类似
conds = group.split(//).inject({:pre => "", :sub => []}) do |conds, g|
          ## do real things
        end

实现的。上面的例子主要为了可读性,并提醒注意else一定要返回cond,否则就会变成nil

关于inject,我想另外拿一个例子与erlang的来作对比。
9 楼 dongbin 2007-02-14  
dcaoyuan 写道
这是另一段代码,是一个直来直去的权限条件判定,返回一个hash给以后的find用:
prvlg_cond = privilege_cond(user, params[:g])
Feed.find(:all, :conditions => [prvlg_cond[:pre], *prvlg_cond[:sub])


有点象erlang了。

  # grp_str: p -> public(0) , u -> user(1), f -> friends(2) 
  def privilege_cond(user, grp_str)
    grp_str ||= 'puf'
    cond = {:pre => "", :sub => []}
    cond = if loggedin?(user)
             frds = grp_str.include?('f') ? user.friends.find(:all) : []
             frd_ids = frds.collect { |frd| frd.friend_id.to_i }
             
             cond = if grp_str.include?('u')
                      {:pre => cond[:pre] + (cond[:pre] == "" ? "" : "OR") + 
                               " user_id  = ? ",
                       :sub => cond[:sub] + [user.id]}
                    else
                      cond
                    end
      
             cond = if grp_str.include?('f') && !frd_ids.empty?
                      {:pre => cond[:pre] + (cond[:pre] == "" ? "" : "OR") + 
                               " user_id in (?) AND privilege in (?) ",
                       :sub => cond[:sub] + [frd_ids, [0, 2]]}
                    else
                      cond
                    end
      
             cond = if grp_str.include?('p')
                      {:pre => cond[:pre] + (cond[:pre] == "" ? "" : "OR") + 
                               " user_id != ? AND privilege  = ? ",
                       :sub => cond[:sub] + [user.id, 0]}
                    else
                       cond
                    end
           else
             {:pre => "privilege = ?",
              :sub => [0]}
           end
  end



上面的代码感觉有点坏味道,可以重构一下么?
8 楼 dcaoyuan 2007-02-10  
Javascript if-else的方案:
var a = function() {
        if (cond())    
           return 1;  
        else  
           return 2;
}()

不知道是不是画了一条用脚走路的蛇,另外对性能可能也有影响。
7 楼 dcaoyuan 2007-02-07  
这是另一段代码,是一个直来直去的权限条件判定,返回一个hash给以后的find用:
prvlg_cond = privilege_cond(user, params[:g])
Feed.find(:all, :conditions => [prvlg_cond[:pre], *prvlg_cond[:sub])


有点象erlang了。

  # grp_str: p -> public(0) , u -> user(1), f -> friends(2) 
  def privilege_cond(user, grp_str)
    grp_str ||= 'puf'
    cond = {:pre => "", :sub => []}
    cond = if loggedin?(user)
             frds = grp_str.include?('f') ? user.friends.find(:all) : []
             frd_ids = frds.collect { |frd| frd.friend_id.to_i }
             
             cond = if grp_str.include?('u')
                      {:pre => cond[:pre] + (cond[:pre] == "" ? "" : "OR") + 
                               " user_id  = ? ",
                       :sub => cond[:sub] + [user.id]}
                    else
                      cond
                    end
      
             cond = if grp_str.include?('f') && !frd_ids.empty?
                      {:pre => cond[:pre] + (cond[:pre] == "" ? "" : "OR") + 
                               " user_id in (?) AND privilege in (?) ",
                       :sub => cond[:sub] + [frd_ids, [0, 2]]}
                    else
                      cond
                    end
      
             cond = if grp_str.include?('p')
                      {:pre => cond[:pre] + (cond[:pre] == "" ? "" : "OR") + 
                               " user_id != ? AND privilege  = ? ",
                       :sub => cond[:sub] + [user.id, 0]}
                    else
                       cond
                    end
           else
             {:pre => "privilege = ?",
              :sub => [0]}
           end
  end

6 楼 whisper 2007-02-06  
begin...end就是个lambda
if cond()那个例子是模式匹配,我只在haskell erlang见过
5 楼 dcaoyuan 2007-02-06  
我的意思是函数式“风格”,尤其是我打算用的erlang的风格。
即使没有上面说的几条,python, javascript和groovy等也都可以有各自的函数式风格,但就是不那么直接了当。

比如在javascript里,if-else子句是不能想象成一个函数的,也就是说你不能写:
var a = if (cond())  
          1;
        else
          2;

当然有其它转换成函数形式的方法(不包括var a = cond() ? 1 : 2;),比如象上面那句:
var a = (cond() && 1) || (true && 2);

这种例子很多人写过。


还有就是ruby的begin-end子句,可以直接把一段“括”起来并返回最后一句作为值,这样就可以随手在ruby代码中写一段函数,而不用先定义lambda,然后再call它。比如上面那一段:
   :limit  => begin  
                 limit = par[:m] ? par[:m].to_i : 20  
                 limit >= 4096 ? 4096 : limit  
              end,  



并行赋值和多值返回跟是否FP确实关系不大,但ruby里的这些内建语法让这些东西写起来比较有我喜欢的风格。如果写一个javascript的例子,也不会那么直接了当滴。

有空我会把找到的erlang和ruby之间的一些对应贴出来。
4 楼 Jan 2007-02-06  
有意思
3 楼 njmzhang 2007-02-06  
    * everything is message(也即任何语句都会返回值)
    * may return multiple values(可以返回多值)
    * parallel assignment(并行赋值
------------------------------------
这些跟FP编程有什么关系?
2 楼 whisper 2007-02-06  
返回多值其实就是个列表
并行赋值倒是有意思,但这不是FP的一部分
1 楼 robbin 2007-02-06  
确实有意思,能多讲讲ruby的FP编程吗?

相关推荐

    基于Ruby编程语言基于Ruby编程语言基于Ruby编程语言基于Ruby编程语言.zip

    5. 支持多种编程范式:除了面向对象,Ruby还支持函数式、命令式和过程式编程,这使得开发者可以根据需求选择合适的编程风格。 6. 标准库丰富:Ruby的标准库包含了大量实用的模块和类,如HTTP客户端、正则表达式处理...

    Ruby的语法类似于英语,易于阅读和理解 它支持多种编程范式,包括面向对象编程、过程式编程和函数式编程

    - **多范式支持**: 支持面向对象、过程式和函数式编程风格,开发者可以根据项目需求选择最适合的方式。 #### 三、Ruby的应用场景 - **Web开发**: 特别是与Ruby on Rails框架结合,可以快速构建高效且功能丰富的Web...

    Programming-Wash-U:编程语言课程-华盛顿大学强调函数式编程ML,Racket和Ruby

    此外,Ruby的元编程能力使得代码能自我描述和自我修改,为实现函数式编程风格提供了可能。在Ruby中,学生将探索如何在面向对象的语言中融合函数式编程的思想,以及如何在实际开发中利用这些概念提高代码的简洁性和...

    函数式-确定性-Ruby取笑___下载.zip

    在Ruby编程语言中,函数式编程是一种编程范式,它强调使用无副作用的纯函数来构建软件。这种编程风格能够帮助开发者写出更加清晰、可预测且易于测试的代码。"函数式-确定性-Ruby取笑___下载.zip"这个文件名暗示了...

    Ruby编程,实用程序员指南Programming Ruby, The Pragmatic Programmer's Guide

    Ruby是一种动态类型语言,支持面向对象编程、函数式编程等多种编程范式。它具有以下特点: - **简洁易读**:Ruby语法简洁明了,易于阅读和理解。 - **动态性**:支持运行时修改代码结构。 - **面向对象**:一切皆...

    Programming Ruby.pdf

    3. **函数式编程**:虽然Ruby主要被视为面向对象的语言,但它也支持函数式编程风格,如使用map、reduce等函数进行数据处理。 4. **多重继承**:Ruby支持多重继承,一个类可以从多个父类继承属性和方法,从而增强了...

    Programming ruby.pdf

    Lambda表达式提供了一种更严格的块定义方式,类似于函数,这在编写函数式风格的代码时特别有用。 《Programming Ruby》还深入探讨了Ruby的库和框架,如ActiveRecord(用于数据库操作)、Test::Unit(单元测试框架)...

    Ruby-Functo是ruby中的可组合方法对象

    总之,Ruby-Functo是Ruby开发中的一个重要工具,尤其对于那些喜欢函数式编程风格的开发者来说,它提供了一套强大的工具集,用于构建可组合、可重用的方法对象,提升了代码的可读性和维护性。通过熟练掌握Functo,...

    笨办法学ruby_笨办法学ruby_ruby_bravevk4_

    Ruby还支持闭包,这是一种强大的工具,可以捕获和存储当前环境的变量,这在函数式编程中尤其有用。 "不墨迹,大量练习"是该教程的一大特点。学习编程不仅仅是理论知识的积累,更重要的是动手实践。书中的每个小节...

    Ruby Programming

    - **多范式支持**:Ruby支持面向对象编程、函数式编程等多种编程风格。 #### 三、Ruby编程书籍推荐——《Programming Ruby》 《Programming Ruby》被誉为是最好的Ruby编程书籍之一,它不仅详尽地介绍了Ruby语言的...

    ruby源代码 ruby源代码 ruby源代码 ruby源代码5

    4. 块和闭包:Ruby中的块是一段可以被传递、存储和执行的代码,与闭包紧密相关,支持函数式编程风格。 5. 自动内存管理:Ruby采用垃圾回收机制,自动处理内存分配和释放,减少了内存泄漏的风险。 6. 标准库:Ruby...

    Programming Ruby

    尽管Ruby主要被归类为面向对象语言,但它也支持函数式编程风格。书中介绍了如何在Ruby中使用闭包、lambda、块等高级功能,以及如何利用这些特性来简化代码,提高程序的可读性和维护性。 #### 4. 元编程 Ruby的元...

    ruby(前途大好的ruby+rains)

    5. **闭包**:Ruby中的 Proc 和 lambda 实现了闭包,能够捕获其定义时的上下文环境,这在函数式编程中非常有用。 6. **模块(Module)**:Ruby的模块用于实现命名空间,代码重用和混合(mixin)功能。一个类可以...

    Learning Functional Programming in Go-Packt Publishing(2017).pdf

    - **错误处理**: 在函数式编程中,可以通过利用单子进行错误处理,同时保持与Go语言原生代码风格的一致性。这种方式可以简化错误处理过程,减少代码的复杂度。 - **性能**: 函数式编程中的引用透明性允许开发者只...

    the ruby way 2ed

    3. **函数式编程**:尽管Ruby主要是面向对象的语言,但它也支持函数式编程风格。书中会介绍闭包、块、Proc对象和Lambda,以及如何利用这些特性实现函数式编程。 4. **元编程**:Ruby的一大特色是元编程能力,它允许...

    ruby2.1.6安装文件

    4. **块和Proc对象**:Ruby 2.1加强了Proc对象,使其可以作为方法参数传递,并且可以捕获局部变量,这在函数式编程风格中非常有用。 5. **垃圾收集**:Ruby 2.1对垃圾回收机制进行了优化,减少了内存占用,提升了...

Global site tag (gtag.js) - Google Analytics