zoukankan      html  css  js  c++  java
  • Ruby中有意思的块

    块:是在调用方法时,能与参数一起传递的多个处理的集合

    简单点说,跟在方法执行后面的do |变量| end就是一个块,这个块会被传入方法中去执行!

    这个非常厉害,非常有意思!

    在ruby中,如果需要便利一个数组,因为Ruby中一切皆是对象,可以使用Array类自身的each方法。
    例如遍历:
    a = [1,2,3,4,5,6]
    只需要使用

    a.each do |one|
    	p one
    end
    

    这里用到就是ruby已经定义好的一个块,那么如何自己弄一个这样的块(自定义)?

    自定义带块的方法

    需要用到一个关键词 yield

    需要了解这个,我们就先写一个each方法出来!如下:

    #为array类添加一个方法myeach
    class Array
        def myeach
            for one in self
    	        #重要的一步
                yield(one)
            end
        end
    end
    
    a = [1,2,3,4,5,6]
    #测试myeach,就像原来自带的each一样了
    a.myeach do |one|
        puts one
    en
    

    注意到,上面自定义部分其实只有三行代码,利用了一个循环(不带块的那种原始方法),遍历的是self,也就是对象本身,中间是“yield”关键词,这很关键!

    你可以这样理解do |one| ~ end部分其实是临时定义了一个匿名方法,并且这个方法被嵌入到了myeach当中,也就是块紧跟着的方法。嵌入的地方就是yield,它替换了yield,并且向doend块中传入了一个参数,就是yield(one)里的one,这是个“形参”,而另一边one就可以在doend中使用了,需要用|one|来接收,这里的one可以改成别的变量,这是个“实参”!

    这就像你在方法中突然嵌入了一个方法,执行了一些代码块一样,只不过,“块”要比方法中调用别的方法强大,灵活多了,各个对象可以根据自己情况来调用方法,传入不一样的值,另外do~end中间的相当于一个临时方法或者有点像闭包(匿名函数),这就使得传入的方法块变的也非常的灵活了,可以临时定义,修改,做出五花八门的功能实现,所以最终被替换的yield也是不确定的,myeach不知道自己将会面临怎样的一个代码块。很有意思

    不定带块情况

    有的时候,开发者可能传入块,可能不传入,这样需要做判断,使用:block_given?

    class Array
        def myeach
    	    #如下改进,判断是否传入了块
            if block_given?
    	        #传入了就要嵌入这个块里的代码,并且向块中传递一个one变量
                for one in self
                    yield(one)
                end
            else
                for one in self
                    p one
                end
            end
        end
    end
    
    a = [1,2,3,4,5,6]
    a.myeach
    puts
    a.myeach do |one|
        puts one*2
    en
    
    

    这里写图片描述

    区别:
    第一种没有块的,就使用myeach默认的实现
    第二种,如果指定了块,就是用块里的方法去做

    带多个参数的块方法

    def block_args_test
        yield()
        yield(1)
        yield(1,2,3)
    end
    
    block_args_test do |a|
        p [a]
    end
    
    block_args_test do |a,b,c|
        p [a,b,c]
    end
    
    block_args_test do |*a|
        p [a]
    en
    

    在block_args_test当中将会调用三次块中的代码
    第一次不传参数,第二次传入一个1,第三次是1,2,3三个参数

    然后看看要用参数的代码块
    第一个就只用一个,如果传入0个参数,则会显示一个nil,以后无论多少个参数都是使用第一个
    第二个同理
    第三个就将接受到的参数转换为一个数组,这与ruby定义方法时接受可变参数情况相似!
    这里写图片描述

    Ruby的块方法与JS的方法变量

    写过js的朋友知道,js中function可以作为对象,即将function赋值给一个变量,然后使用变量来调用方法,因为变量是可以传递的,所以就使得我们可以轻松的在js中传递方法!

    Ruby不可以传递一个def的方法,但是可以使用block来实现,也就是块方法

    上面所介绍的都是紧跟在方法后面的“匿名块方法”,也就是传入一次后,等到执行结束就不用了,如果我们要在多个方法中调用同一个块方法,就需要用到块方法的对象!(像js一样传递对象变量)

    块方法赋值给对象,简单的例子:

    show = Proc.new do |res|
        p res
    end
    

    Proc能让块变成对象!
    这里使用Proc.new将紧跟其后的代码块交给了变量show
    这相当于js的:

    show = function(res){
    	console.log(res);
    }
    

    调用他使用call:

    show.call("hello world")
    

    这里写图片描述

    和前面的一样,将他传入其他方法中!

    show = Proc.new do |res|
        p res
    end
    
    def plus(a,b,&block)
        block.call(a+b)
    end
    
    plus(1,5,&show)
    

    使用时注意两个地方:

    1. 定义方法时,最后一个参数添加“&”符号,表示传入的是方法块对象
    2. 传入时也要添加“&”与定义保持一致

    如此即可轻松的传递方法(没有js那么灵活)

    其实仔细一想,Ruby中的块方法就像是js的回调函数不是吗?闭包不是吗?

    一种是匿名的:
    直接在方法后面紧跟这代码块do~end表示传入的回调,当然方法中必须要有yield明确调用的地点,参数等

    另一种是对象的:
    将方法块通过Proc.new赋值给一个变量,然后通过&变量传递到其他的方法中实现回调

  • 相关阅读:
    我的浏览器收藏夹分类
    我的浏览器收藏夹分类
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
  • 原文地址:https://www.cnblogs.com/devilyouwei/p/6771593.html
Copyright © 2011-2022 走看看