• 元编程
    • yield

      所有的"方法(methods)"隐式跟上一个"块(block)"参数。

      块参数也可以明确给定,形式就是在参数前面加一个"&",比如 def fn(arg1, arg2, &block) end,其中的 &block 就是明确给定的块参数。

      块参数的动作,可以通过调用 call() 方法执行,还可以用 yield 来执行 —— yield 其实就是一个语法糖。

      所以以下几种写法常常是等价的:

      #method receives an invisible block argument
      def foo1()
      yield 1
      end #specify it explicitly
      def foo2(&block)
      yield 1
      end #yield is equal to block.call
      def foo3(&block)
      block.call(1)
      end #function call
      foo1 {|x| puts x} # => 1
      foo2 {|x| puts x} # => 1
      foo3 {|x| puts x} # => 1

      注意事项
      method 定义中 &block 参数必须在最后

      yield self

      在一个对象中,self 表示是一个当前对象的引用。

      所以,常见的 yield self if block_given? 中的 self 就和其它地方使用 self 一样,没什么特殊的。

      Proc

      前面说到所有方法都可以隐式或显式指定一个块参数,那么块参数到底是什么呢?

      答案是 Proc 对象,一个具有 call 方法的对象。

      Proc 对象的定义有几种形式:

      • 直接使用 {}
      • 使用 Proc.new {}
      • 使用 proc {}
      • 使用 lambda {}
      #yield is equal to block.call
      def foo(&block)
      puts block.class
      puts block.to_s
      yield 1
      end #function call
      # Proc created using {} syntax
      foo {|x| puts x}
      # => Proc
      # => #<Proc:0x00000000e0b140@(ruby):9>
      # => 1 # Proc created with the "proc" keyword. Note & syntax when calling.
      my_proc = proc { |n| puts n }
      foo(&my_proc)
      # => Proc
      # => #<Proc:0x00000000e0b140@(ruby):12>
      # => 1 # Proc creates with Proc.new
      my_proc = Proc.new { |n| puts n }
      foo(&my_proc) # => 1
      # => Proc
      # => #<Proc:0x00000000e0b140@(ruby):16>
      # => 1 # Proc created with the "lambda" keyword. Nearly same thing.
      my_proc = lambda { |n| puts n }
      foo(&my_proc)
      # => Proc
      # => #<Proc:0x00000000e0b140@(ruby):20 (lambda)>
      # => 1

      本节原文

      yield带参数

      def many_yields
      yield(:peanut)
      yield(:butter)
      yield(:and)
      yield(:jelly)
      end def test_methods_can_call_yield_many_times
      result = []
      many_yields { |item| result << item } # result作用域
      assert_equal [:peanut, :butter, :and, :jelly], result
      end

      rails中:yield 和 content_for

      在View布局中,yield 标明一个区域,渲染的视图会插入这里。最简单的情况是只有一个 yield,此时渲染的整个视图都会插入这个区域:

      <html>
      <head>
      </head>
      <body>
      <%= yield %>
      </body>
      </html>

      布局中可以标明多个区域:

      <html>
      <head>
      <%= yield :head %>
      </head>
      <body>
      <%= yield %>
      </body>
      </html>

      视图的主体会插入未命名的 yield 区域。若想在具名 yield 区域插入内容,要使用 content_for 方法。

      <% content_for :head do %>
      <title>A simple page</title>
      <% end %> <p>Hello, Rails!</p>

      套入布局后生成的 HTML 如下:

      <html>
      <head>
      <title>A simple page</title>
      </head>
      <body>
      <p>Hello, Rails!</p>
      </body>
      </html>

      methods、proc、lambda、block

      • block和proc都不检查参数,methods和lambda会检查参数
      • block和proc是两种不同的东西, block有形无体,proc可以将block实体化, 可以把&p看做一种运算,其中&触发p的to_proc方法,然后&会将to_proc方法返回的proc对象转换成block 。
      • &p是block, p是proc,不到万不得已的情况下不要显式地创建proc
      • lambda是匿名方法, lambda和proc也是两种不同的东西,但是在ruby中lambda只能依附proc而存在,这点和block不同,block并不依赖proc。
      • lambda和proc之间的区别除了那个经常用做面试题目的经典的return之外,还有一个区别就是lambda不能完美的转换为block(这点可以通过f3和f4执行的过程得证),而proc可以完美的转换为block,注意,我说的lambda指的是用lambda方法或者->符号生成的proc,当然和方法一样lambda是严格检查参数的,这个特点也和proc不一样。

      本节详文

      闭包(用proc延长变量的生命周期)

      有两只途径实现:
      闭包创建了它所需要的所有变量的一个备份, 因此是这些副本随着闭包传递.
      闭包 延长了它所需要的所有变量的生命周期. 没有复制变量, 而是保留了它们的引用, 而且变量本身不可以被垃圾回收器回收掉.
      如果语言支持第一种方式, 那么如果我们创建两个或者更多闭包来访问相同的变量, 每个闭包被调用时都有自己单独对变量的拷贝. 如果语言支持第二中方式, 所有的闭包都引用同一个变量, 它们实际上处理的就是同一变量. Ruby 就是这么做的.看下面的例子:

      class SomeClass
      def initialize(value1)
      @value1 = value1
      end def value_incrementer
      lambda { @value1 += 1 }
      end def value_printer
      lambda { puts "value: #{ @value1 }"}
      end
      end some_class = SomeClass.new(2)
      incrementer_closure = some_class.value_incrementer
      printer_closure = some_class.value_printer
      3.times do
      incrementer_closure.call
      printer_closure.call
      end
      运行结果:
      #=>
      value: 3
      value: 4
      value: 5

      本节原文

      method_missing终止对祖先链(ancestors)的查找

      什么情况下需要使用?

      p、puts、print

      class T
      def initialize(i)
      @i = i
      end
      def to_s
      @i.to_s
      end
      end t = T.new 42
      puts t => 42
      p t => #<T:0xb7ecc8b0 @i=42>
      • p、puts 自动换行,print不自动换行
      • p有返回值, puts、print返回nil

      正则、字符相关特殊符号

      • %{String} 用于创建一个使用双引号括起来的字符串
      • %Q{String} 用于创建一个使用双引号括起来的字符串
      • %q{String} 用于创建一个使用单引号括起来的字符串
      • %r{String} 用于创建一个正则表达式字面值
      • %w{String} 用于将一个字符串以空白字符切分成一个字符串数组,进行较少替换
      • %W{String} 用于将一个字符串以空白字符切分成一个字符串数组,进行较多替换
      • %s{String} 用于生成一个符号对象
      • %x{String} 用于执行String所代表的命令

      大写的支持变量替换

      rubular正则

      Module 是 Class 的基类,但Module不能实例化,并且只能与类一同使用

      异常

      throw 和 catch功能类似goto 在ruby不是用来处理异常的

      begin
      f = File.open("ruby.txt")
      # .. continue file processing
      rescue ex => Exception
      # .. handle errors, if any
      ensure
      f.close unless f.nil?
      # always execute the code in ensure block
      end

      元编程

      元编程能力能够让程序员编写在运行时动态生成的代码。
      它的线程功能使得程序员有一种优雅的的方式编写多线程代码。
      它的钩子方法能让程序员在程序运行时扩展它的行为。

      《Ruby元编程》待读
      元编程简述

      类打开,添加新方法,如何删减方法

      删除类方法的途径有2个,一个是调用Module#undef_method方法,一个是调用Module#remove_method方法。

      创建单例方法

      class A
      end a = A.new
      def a.f1
      puts 'f1'
      end class << a
      def f2
      puts 'f2'
      end
      end

      创建类方法

      class SelfTest
      def self.test
      puts "Hello World with self!"
      end
      end class SelfTest2
      def test
      puts "This is not a class static method"
      end
      end SelfTest.test # works fine
      SelfTest2.test #error class TestMe
      def TestMe.test
      puts "Yet another static member function"
      end
      end TestMe.test # works fine class MyTest
      class << self #创建/打开一个元类(MyTest是class实例),为其添加单例方法
      def test
      puts "This is a class static method"
      end
      end
      end MyTest.test # works fine
      MyTest.new.test # error #其他方式创建类方法
      class A
      end
      (class << A; self; end).class_eval do
      def you
      p 'you'
      end
      end A.singleton_class.class_eval do
      def fun
      p 'fun'
      end
      end
      变量归属
      class B
      @a=1 #B.instance_variables
      @@b=2 #B.class_variables def initialize
      @c = 3 #b.instance_variables
      @@d = 4#B.class_variables
      end class << self
      @e = 5 #B.singleton_class.instance_variables
      @@f = 6#B.class_variables
      end
      end

      self

      self作用域

      class A
      class<<self #
      self #
      end
      end
      不能用于私有方法:对私有方法的调用只能针对隐式对象。
      class A
      def p1
      self.s1
      end
      def p2
      s1
      end
      private
      def s1
      puts "private def "
      end
      end a = A.new
      a.p1 #error
      a.p2 # ok
      a.s1 #error

      send 是 Object 类的一个公共方法

      • 可用于访问一个类的私有方法

      魔法太多,如何调试

      201706 Ruby 基础 & 元编程的更多相关文章

      1. Ruby元编程:动态添加类属性及其实际应用

        上个星期测试道的Monkey老师和我聊到测试用例参数过多的问题,其实这样的问题在我这里也同样经历过.比如我的测试用例必须面对不同的测试环境,每个环境有无数的参数,开发的最初阶段,因为参数少,所以就放在 ...

      2. C++值元编程

        --永远不要在OJ上使用值元编程,过于简单的没有优势,能有优势的编译错误. 背景 2019年10月,我在学习算法.有一道作业题,输入规模很小,可以用打表法解决.具体方案有以下三种: 运行时预处理,生成 ...

      3. 3-11 《Ruby元编程》第4章block块 3-12

        第4章代码块blocks 基础知识 作用域:用代码块携带variables through scopes 通过传递block给instance_eval方法来控制作用域. 把block转换为Proc, ...

      4. 3-8《Ruby元编程》第二章对象模型

        <Ruby元编程> 第二章 对象模型 类定义揭秘inside class definitions: class关键字更像一个作用域操作符,核心作用是可以在里面随时定义方法. [].meth ...

      5. Ruby元编程:单元测试框架如何找到测试用例

        前几天看了Google Testing Blog上的一篇文章讲到C++因为没有反射机制,所以如何注册测试用例就成了一件需要各显神通的事情.从我的经验来看,无论是Google的GTest还是微软的LTM ...

      6. 201707《Ruby元编程》

        元编程不过是编程--经典必读 作用域(绑定) 打破作用域门的方式 对象模型图 七条规则 法术手册 作用域(绑定) 改变作用域的关键字, 分别是module,class和def.我们称为作用域的门(sc ...

      7. 翻译 - 元编程动态方法之public_send

        李哲 - MAY 20, 2015 原文地址:Metaprogramming Dynamic Methods: Using Public_send 作者:Friends of The Web的开发者V ...

      8. Groovy元编程简明教程

        同函数式编程类似,元编程,看上去像一门独派武学. 在 <Ruby元编程>一书中,定义:元编程是运行时操作语言构件的编程能力.其中,语言构件指模块.类.方法.变量等.常用的主要是动态创建和访 ...

      9. C++模板元编程(C++ template metaprogramming)

        实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...

      随机推荐

      1. [理解] C++ 中的 源文件 和 头文件

        我是学 C井 的, 现在在工作中主要使用的编程语言是 Java, 还记得当初在第一次接触到 Cpp 的时候, 听到的第一个概念就是 Cpp 的头文件和源文件, 当初理解了好久, 死活都弄不明白, 现在 ...

      2. nginx 重写 隐藏index.php

        修改 nginx.conf 文件location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; break ...

      3. Flask程序相关配置加载的三种方式

        方式一:从对象中加载配置 1.定义配置类,在配置类中添加相应的配置 2.通过app.config.from_object(配置类)进行加载 代码如下: from flask import Flask ...

      4. js keyCode 常用键盘编码

        摘自:http://blog.csdn.net/dyllove98/article/details/8728657 keycode 8 = BackSpace BackSpace keycode 9 ...

      5. npm安装包时报错:Error: EPERM: operation not permitted, rename

        解决方法:先执行 npm cache clean -force在安装需要的包.

      6. Java解析json数组三种情况

        package com.example.demo.json; import java.util.Map; import com.alibaba.fastjson.JSON; import com.al ...

      7. HackerOne去年发放超过8200万美元的赏金,联邦政府参与度大幅上涨

        2019年,由黑客驱动的漏洞赏金平台HackerOne支付的漏洞奖金几乎是前几年总和的两倍,达到8200万美元. HackerOne平台在2019年也将注册黑客数量翻了一番,超过了60万,同时全年收到 ...

      8. Spring 注意事项

        1.在我们使用spring 5.x版本的时候,要求junit 的jar版本是4.12及以上. 2.不管是什么样的配置,当发现之前能用,改了位置就不能用的时候,首先要考虑的问题就是:是否有约束上顺序的要 ...

      9. docsify简单教程

        简介 一个神奇的文档网站生成器. 简单而轻便(〜18kB压缩) 没有静态构建的HTML文件 多个主题 快速开始 建议docsify-cli全局安装,这有助于本地初始化和预览网站. npm i docs ...

      10. [Write-up]-pwnlab_init

        关于 下载地址点我 Flag: /root/flag.txt 放假的第一天 哔哩哔哩视频 信息收集 nmap -sn 192.168.7.1/24 Starting Nmap 7.01 ( https ...