Ruby的Enumerable#zip是否在内部创建数组?

据说,在 Ruby – Compare two Enumerators elegantly

The problem with zip is that it
creates arrays internally,no matter
what Enumerable you pass. There’s
another problem with length of input
params

我看了一下YARV中Enumerable#zip的实现,并看到了

static VALUE
enum_zip(int argc,VALUE *argv,VALUE obj)
{
    int i;
    ID conv;
    NODE *memo;
    VALUE result = Qnil;
    VALUE args = rb_ary_new4(argc,argv);
    int allary = TRUE;

    argv = RARRAY_PTR(args);
    for (i=0; i<argc; i++) {
        VALUE ary = rb_check_array_type(argv[i]);
        if (NIL_P(ary)) {
            allary = FALSE;
            break;
        }
        argv[i] = ary;
    }
    if (!allary) {
        CONST_ID(conv,"to_enum");
        for (i=0; i<argc; i++) {
            argv[i] = rb_funcall(argv[i],conv,1,ID2SYM(id_each));
        }
    }
    if (!rb_block_given_p()) {
        result = rb_ary_new();
    }
    /* use NODE_DOT2 as memo(v,v,-) */
    memo = rb_node_newnode(NODE_DOT2,result,args,0);
    rb_block_call(obj,id_each,allary ? zip_ary : zip_i,(VALUE)memo);

    return result;
}

我是否正确理解了以下位?

检查是否所有参数都是数组,如果是,则使用直接引用替换对数组的某些间接引用

for (i=0; i<argc; i++) {
        VALUE ary = rb_check_array_type(argv[i]);
        if (NIL_P(ary)) {
            allary = FALSE;
            break;
        }
        argv[i] = ary;
    }

如果它们不是所有数组,请改为创建枚举器

if (!allary) {
        CONST_ID(conv,ID2SYM(id_each));
        }
    }

仅在未给出块时才创建数组数组

if (!rb_block_given_p()) {
        result = rb_ary_new();
    }

如果一切都是数组,请使用zip_ary,否则使用zip_i,并在每组值上调用一个块

/* use NODE_DOT2 as memo(v,(VALUE)memo);

如果没有给出块,则返回一个数组数组,否则返回nil(Qnil)?

return result;
}

解决方法

我将使用1.9.2-p0,就像我手边的东西一样.

rb_check_array_type函数如下所示:

VALUE
rb_check_array_type(VALUE ary)
{
    return rb_check_convert_type(ary,T_ARRAY,"Array","to_ary");  
}

并且rb_check_convert_type看起来像这样:

VALUE
rb_check_convert_type(VALUE val,int type,const char *tname,const char *method)
{
    VALUE v;

    /* always convert T_DATA */
    if (TYPE(val) == type && type != T_DATA) return val;
    v = convert_type(val,tname,method,FALSE);
    if (NIL_P(v)) return Qnil;
    if (TYPE(v) != type) {
        const char *cname = rb_obj_classname(val);
        rb_raise(rb_eTypeError,"can't convert %s to %s (%s#%s gives %s)",cname,rb_obj_classname(v));
    }
    return v;
}

请注意convert_type调用.这看起来很像C版的Array.try_convert和try_convert恰好看起来像这样:

/*   
 *  call-seq:
 *     Array.try_convert(obj) -> array or nil
 *
 *  Try to convert <i>obj</i> into an array,using +to_ary+ method. 
 *  Returns converted array or +nil+ if <i>obj</i> cannot be converted
 *  for any reason. This method can be used to check if an argument is an
 *  array.
 *   
 *     Array.try_convert([1])   #=> [1]
 *     Array.try_convert("1")   #=> nil
 *
 *     if tmp = Array.try_convert(arg)
 *       # the argument is an array
 *     elsif tmp = String.try_convert(arg)
 *       # the argument is a string
 *     end
 *
 */
static VALUE
rb_ary_s_try_convert(VALUE dummy,VALUE ary)
{
    return rb_check_array_type(ary);
}

所以,是的,第一个循环是在argv中查找不是数组的任何东西,如果找到这样的东西,则设置allary标志.

在enum.c中,我们看到:

id_each = rb_intern("each");

所以id_each是Ruby每个迭代器方法的内部引用.在vm_eval.c中,我们有:

/*!  
 * Calls a method 
 * \param recv   receiver of the method
 * \param mid    an ID that represents the name of the method
 * \param n      the number of arguments
 * \param ...    arbitrary number of method arguments  
 *
 * \pre each of arguments after \a n must be a VALUE.
 */
VALUE
rb_funcall(VALUE recv,ID mid,int n,...)

所以这:

argv[i] = rb_funcall(argv[i],ID2SYM(id_each));

在argv [i]中的任何内容上调用to_enum(基本上是default argument).

因此,第一个for和if块的最终结果是argv要么充满了数组,要么充满了枚举数,而不是两者的混合.但请注意逻辑是如何工作的:如果找到的东西不是数组,那么一切都变成了枚举器. enum_zip函数的第一部分将数组包装在枚举器中(基本上是免费的或至少足够便宜而不用担心)但不会将枚举器扩展为数组(这可能非常昂贵).早期的版本可能已经走了另一条路(更喜欢数组而不是枚举数),我将把它作为读者或历史学家的练习.

下一部分:

if (!rb_block_given_p()) {
    result = rb_ary_new();
}

如果在没有块的情况下调用zip,则创建一个新的空数组并将其保留在结果中.在这里我们应该注意zip returns

enum.zip(arg,...) → an_array_of_array
enum.zip(arg,...) {|arr| block } → nil

如果有一个区块,则没有任何东西可以返回,结果可以保持为Qnil;如果没有块,那么我们需要一个结果中的数组,以便可以返回一个数组.

从parse.c,我们看到NODE_DOT2是一个双点范围,但看起来他们只是将新节点用作一个简单的三元素结构; rb_new_node只是分配一个对象,设置一些位,并在结构中分配三个值:

NODE*
rb_node_newnode(enum node_type type,VALUE a0,VALUE a1,VALUE a2)
{
    NODE *n = (NODE*)rb_newobj();

    n->flags |= T_NODE;
    nd_set_type(n,type);

    n->u1.value = a0;
    n->u2.value = a1;
    n->u3.value = a2;

    return n;
}

nd_set_type只是一个小小的宏.现在我们将备忘录作为三元素结构.这种NODE_DOT2的使用似乎是一种方便的方法.

rb_block_call函数似乎是核心内部迭代器.我们再次看到我们的朋友id_each,所以我们将进行每次迭代.然后我们看到zip_i和zip_ary之间的选择;这是创建内部数组并将其推送到结果的位置. zip_i和zip_ary之间的唯一区别似乎是zip_i中的StopIteration异常处理.

此时我们已经完成了压缩,我们要么在结果中有数组数组(如果没有块),要么我们在结果中有Qnil(如果有块).

执行摘要:第一个循环明确避免将枚举数扩展到数组中.如果zip_i和zip_ary调用必须构建一个数组数组作为返回值,则它们只能用于非临时数组.因此,如果您使用至少一个非数组枚举器调用zip并使用块形式,那么它一直是枚举器,并且“zip的问题是它在内部创建数组”不会发生.回顾1.8或其他Ruby实现是留给读者的练习.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


validates:conclusion,:presence=>true,:inclusion=>{:in=>[0,1]}validates:email,:presence=>true,:length=>{:minimum=>3,:maximum=>254},:uniqueness=>true,:email=>truevalidates:ending_order,
一、redis集群搭建redis3.0以前,提供了Sentinel工具来监控各Master的状态,如果Master异常,则会做主从切换,将Slave作为master,将master做为slave。其配置也较复杂,且表现一般。redis3.0以后已经支持集群容错功能,并且非常简单1.1素材准备centos7(集群搭建,至少三个master。需
分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow                 不知道大家是否注意到,全新安装ruby之后,无论是在windows或是linux还是macosX下使用rix
上一篇博文 ruby传参之引用类型 里边定义了一个方法名 modify_my_object!,这个方法名是以!结尾,在ruby的对象里边是用于表达修改本身的意思。比如String#gsub,返回的是一个新的字符串对象;而String#gsub!,返回的是自身已经被修改的对象。不止!这样的特殊字符可以命名,ruby
一编程与编程语言 什么是编程语言? 能够被计算机所识别的表达方式即编程语言,语言是沟通的介质,而编程语言是程序员与计算机沟通的介质。 什么是编程? 编程即程序员根据需求把自己的思想流程按照某种编程语言的语法风格编写下来,产出的结果就是包含一堆字符的文件。二编程语言分
Ruby类和对象Ruby是一种完美的面向对象编程语言。面向对象编程语言的特性包括:数据封装数据抽象多态性继承这些特性将在面向对象的Ruby中进行讨论。一个面向对象的程序,涉及到的类和对象。类是个别对象创建的蓝图。在面向对象的术语中,您
1.ruby的标签<ruby>漢<rp>(<p><rt>han<t><rp>)<p>字<rp>(<p><rt>zi<t><rp>)<p><uby> 
1、软件安装1.安装包是个压缩包-->解压到/homeedis_tar下命令如下: tar-zxvf./../-C/homeedis_tar2.安装c++环境yum-yinstallgcc-c++(注:redis底层源码是c++)3.解压后需要源码编译进入到redis-3.0.0后执行make4.编译完后开始安装需要指定一个安装路径
1.sass基于Ruby语言开发而成,因此安装sass前需要安装Ruby。(注:mac下自带Ruby无需在安装Ruby!)window下安装SASS首先需要安装Ruby,先从官网下载Ruby并安装。安装过程中请注意勾选AddRubyexecutablestoyourPATH添加到系统环境变量。ruby官网:https:/ubyinstaller.org/downloa
本节对我们项目实现的功能和知识点做一个简单的介绍,因为是RESTfulAPI项目,所以对于后端来说基本上没有什么UI界面可展示,那我们就在关键的点,使用客户端(Android)实现的效果图。课程简介这是一门企业级项目实战课程,目的是从0使用Rails开发一个企业级RESTfulAPI项目;他不能让你年薪30
注意点:1.redis集群至少需要6个节点。2.redis集群至少部署在3台服务器上。3.redis的版本需要在3.0以上。4.需要ruby的支持。步骤:1.安装依赖环境:yuminstallgcc-c++2.下载ruby-2.4.6,编译安装https://cache.ruby-lang.org/pububy/2.4uby-2.4.6.tar.gztarzxf ruby-2.4.6.
在我的客户项目中,我看到很多睡眠用法0.代码看起来像这样.whiletrue......sleep0end通过阅读SO的一些答案,如this,似乎睡眠0具有一定的意义.我现在想知道的是,在时间片0期间调度其他线程运行(如果它们正在等待运行)是像ruby或python这样的langVM的工作,或者它
前情提要:第11天开始,要更深入Ruby的精髓!Ruby经典面试题目#11Ruby的block,proc,lamdba方法比较?What’sdifferencebetweenblocks,procsandlambdas?block代码内存块代码内存块是用do…end围起来,围出特定一个区域、放代码的地方。就好像跑马拉松一样,道路上会进行交通管制,把参赛者
安装环境及工具系统:RedHatEnterpriseLinuxServer工具:XShell5及Xftp5等远程工具安装包:Ruby-2.4.1及以上       Rubygems-2.6.12及以上       Redis-3.2.8及以上(3.x版本才开始支持集群功能)       Redis-3.0.0-gem及以上(该版本不一定要和red
 今天在做Redis的Cluster集群的时候,在执行geminstallredis时,提示如下错误:geminstallredisERROR:Errorinstallingredis:redisrequiresRubyversion>=2.2.2.CentOS7yum库中ruby的版本支持到2.0.0,可gem安装redis需要最低是2.2.2,采用rvm来更新ruby:1
我试图在Rails4中制作应用程序.我正在挣扎.我正在尝试合并一个bootstrap主题,我遇到了供应商javascripts和其余代码的问题.我认为问题可能与在我的application.js中使用jQuery然后使用以’$’符号开头的供应商.js文件有关:$.circleProgress={我刚看过这个:https://learn.jqu
该内容全部为搬运,感谢作者的分享~,附有原文链接。使用ruby环境SASS学习系列之(一)---------SASS,SCSS环境搭建(Ruby) 使用node-sassSASS学习系列之(二)---------SASS,SCSS环境搭建(node-sass)通过命令编译:npmrunsassinput.scssoutput.csssass-loader在webpack打包里
我如何使用PHP5.3Closures,比如我们在Ruby中使用Blocks.我从来没有在Ruby中使用’for’循环,因为使用带有’each”read_all”inject’方法的块.我如何使用像Ruby块这样的PHP5.3闭包,并说再见’for’Loops
一、说明:搭建Redis集群时,安装geminstallredis报错: redisrequiresRubyversion>=2.2.2的报错,查了资料发现是Centos默认支持ruby到2.0.0,可gem安装redis需要最低是2.2.2二、解决办法:解决办法是先安装rvm,再把ruby版本提升至2.3.31.安装curlsudoyuminstal
compass.app是集成了sass的工具,安装完Compass就能够使用sass。首先,上官网 可以看到官网上推荐的两种sass使用方式,application&commandlineapplication里不仅仅只有Compass,不过现在点击Compass.app进行下载。虽然通常我们说sass运行在Ruby环境下,但Compass.app安装使用并不需要