msn email google-talk twitter tumblr flickr

Ruby学习笔记:代码块(block)与Proc对象

首先要说明的是 代码块实际上是Proc对象 使用这个标题只是为了阐明2者之间的关系
   1      p = Proc.new{puts "foo"}
   2      p.call

以上是一个典型的Proc对象的初始化及调用 来看看常用的block用法

   1      def test
   2           puts "before the yield"
   3           yield
   4           puts "after the yield"
   5      end
   6      test{puts "bar"}

输出:

    before the yield
    bar
    after the yield

我们所做的是在运行时向test方法中传入一个代码块(block)作为参数 而test方法在定义时 我们并没有定义参数列表 所以这个参数是隐藏的 然后yield关键词执行了这个隐藏的参数(代码) 如果传入的代码块具有参数 那么只需要再yield时加上参数即可

运行方式
   1      def test
   2           puts "before the yield"
   3           yield
   4           puts "after the yield"
   5      end
   6      test{puts "bar";return}
   7  
   8      def test(&block)
   9           puts "before the yield"
  10           block.call
  11           puts "after the yield"
  12      end
  13      test{puts "bar";return}
  14  
  15      def test(p)
  16           puts "before the yield"
  17           p.call
  18           puts "after the yield"
  19      end
  20      p=Proc.new{puts "bar";return}
  21      test(p)

以上三种都报错: LocalJumpError: unexpected return

   1      def test(p)
   2           puts "before the yield"
   3           p.call
   4           puts "after the yield"
   5      end
   6      p=lambda{puts "bar";return}
   7      test(p)
   8  
   9      def test(p)
  10           puts "before the yield"
  11           p.call
  12           puts "after the yield"
  13      end
  14      p=proc{puts "bar";return}
  15      test(p)

以上两种纯参数方式传入 正常输出

闭包特性
   1      def test
   2           a = "bbb"
   3           puts a
   4           yield
   5           puts a
   6      end
   7      a = "aaa"
   8      test{puts a}
   9  
  10      def test(&block)
  11           a = "bbb"
  12           puts a
  13           block.call
  14           puts a
  15      end
  16      a = "aaa"
  17      test{puts a}
  18  
  19      def test(p)
  20           a = "bbb"
  21           puts a
  22           p.call
  23           puts a
  24      end
  25      a = "aaa"
  26      p = Proc.new{puts a}
  27      test(p)

输出结果都为:

    bbb
    aaa
    bbb
关于yield和call

yield是隐式的调用 call是Proc对象的显示调用

   1      def test(&p) <==
   2           a = "bbb"
   3           puts a
   4           yield
   5           p.call
   6           puts a
   7      end
   8      a = "aaa"
   9      test{puts a}

从代码块转为Proc 从而支持显式的调用

   1      def test(&p)
   2           a = "bbb"
   3           puts a
   4           p.call
   5           yield
   6           puts a
   7      end
   8      a = "aaa"
   9      p = Proc.new{puts a}
  10      test(&p) <==

从Proc对象转为代码块(block) 使得在方法体内部支持隐式的调用

以上两个例子输出结果都为:

    bbb
    aaa
    aaa
    bbb

我们可以看出block和Proc对象是可以互相转化的(使用&符号) 而代码块(block)本身是作为隐式的参数 无论是使用隐式的调用还是显式调用都可以正常运行

   1      def test
   2           a = "bbb"
   3           puts a
   4           yield <==
   5           puts a
   6      end
   7      a = "aaa"
   8      test{puts a}

不能使用call显式调用

   1      def test(p)
   2           a = "bbb"
   3           puts a
   4           p.call <==
   5           puts a
   6      end
   7      a = "aaa"
   8      p = Proc.new{puts a}
   9      test(p)

没有使用代码块(block)作为参数 仅支持Proc对象本身的显式调用