zoukankan      html  css  js  c++  java
  • 第一个ruby程序

    老实说不是很喜欢去讨论ruby和python的对比,似乎总是把两个语言放在对立的位置上,我觉得没有必要,同样是动态语言,同样是解释型脚本语言,很多特性都是互相影响的,语言本身也在不断进化,我们更应该关注的是编程思想而不是语言本身。

    说了一点题外话,之所以要求学ruby,主要就是因为有一本好书想读,就是Paolo Perrotta的《Ruby元编程》,书看了一天,ruby语法学了半天,用irb捣鼓了一晚上,应该说跟python,scala都有很多相近的地方,因为没有接触Lisp,Haskell之类的函数编程语言,因此不好说跟他们怎么样,但是学了python再去学ruby是觉得非常轻松地。

    书中星期二中有个问题挑战,就是对DS和Computer类中的函数进行动态生成,好吧,我又犯老毛病了,书看一半,自己先干,在看到define_method方法后用我自己的思路捣鼓了一个解决方案,还没看书中的实例,先把代码弄出来看看,以下就是我的第一个ruby程序:

    # 数据源定义,原应为关联数据库,这里只是进行模拟
    class DS
      parts = 'cpu', 'mouse', 'keyboard'  # 临时数组用于批量生成方法
      parts.each do |part|
        define_method "get_#{part}_info".to_sym do |id| # 生成三种设备的信息函数
          "This is #{part} #{id}."
        end
        define_method "get_#{part}_price".to_sym do |id|  # 生成三种设备的价格函数
          parts.zip([150, 50, 40]).to_h[part] # 通过zip合并数组转换为哈希结构再索引
        end
      end
    end
    # 电脑配件定义
    class Computer
      def initialize(computer_id, data_source)  # 初始化id和数据源
        @id = computer_id
        @data_source = data_source
      end
      parts = 'cpu', 'mouse', 'keyboard'
      parts.each do |part|
        define_method part.to_sym do
          info = @data_source.send("get_#{part}_info".to_sym, @id)
          price = @data_source.send("get_#{part}_price".to_sym, @id)
          result = "#{info} ($#{price})"
          return " * #{result}" if price >= 100 # 判断价格是否高于100,加星号显示
          result
        end
      end
    end
    puts "DS methods: #{DS.instance_methods(false)}"
    puts "Computer methods: #{Computer.instance_methods(false)}"
    
    ds = DS.new
    computer = Computer.new(10, ds)
    puts computer.cpu
    puts computer.mouse
    puts computer.keyboard

     然后继续看书,在看到内省的用法的时候觉得比较好,想把我上面的程序修改一下,于是改成了这个样子:

    # 电脑配件定义
    class Computer
      def initialize(computer_id, data_source)  # 初始化id和数据源
        @id = computer_id
        @data_source = data_source
        data_source.methods.grep(/^get_(.*)_info$/) {
          define_method($1.to_sym) {
            info = @data_source.send("get_#{$1}_info", @id)
            price = @data_source.send("get_#{$1}_price", @id)
            result = "#{info} ($#{price})"
            return " * #{result}" if price >= 100 # 判断价格是否高于100,加星号显示
            result
          }
        }
      end
    end

    运行的时候报错了,然后才发现define_method是Class的私有函数,因为属于Class所以不能在实例函数中使用,而因为私有,所以只能隐式调用,如果想这么用只能通过send来进行调用,所以把程序改成了下面这个样子

    class Computer
      def initialize(computer_id, data_source)  # 初始化id和数据源
        @id = computer_id
        @data_source = data_source
        data_source.methods.grep(/^get_(.*)_info$/) {
          Computer.send(:define_method, $1) {
            info = @data_source.send("get_#{$1}_info", @id)
            price = @data_source.send("get_#{$1}_price", @id)
            result = "#{info} ($#{price})"
            return " * #{result}" if price >= 100 # 判断价格是否高于100,加星号显示
            result
          }
        }
      end
    end

    很不幸,又出问题了,这次出在变量$1未被正确识别,原因是$1是全局变量,而ruby是动态解析的,只有在运行的时候才去获取$1的值,而那个时候它已经编程nil了,因此将全局变量保存为局部变量

    # 电脑配件定义
    class Computer
      def initialize(computer_id, data_source)  # 初始化id和数据源
        @id = computer_id
        @data_source = data_source
        data_source.methods.grep(/^get_(.*)_info$/) {
          part = $1
          Computer.send(:define_method, part) {
            info = @data_source.send("get_#{part}_info", @id)
            price = @data_source.send("get_#{part}_price", @id)
            result = "#{info} ($#{price})"
            return " * #{result}" if price >= 100 # 判断价格是否高于100,加星号显示
            result
          }
        }
      end
    end

    其实是有点丑了,不过总要写得跟原文不太一样啦,对于ruby还有不少需要对比和理解,希望看完这本书能好一些

    
    
  • 相关阅读:
    适配器模式
    显示实现接口
    Mysql表引擎的切换
    Mysql事务隔离级别
    按照指定的格式解析字节数组
    委托和事件的简单实用
    C#压缩和解压缩字节(GZip)
    Mysql数据库批量添加数据
    常用的分页类
    保证依赖的服务已全部启动
  • 原文地址:https://www.cnblogs.com/lancky/p/6189519.html
Copyright © 2011-2022 走看看