热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[Erlang0116]当我们谈论ErlangMaps时,我们谈论什么Part1

Erlang增加Maps数据类型并不是很突然,因为这个提议已经进行了2~3年之久,只不过JoeArmstrong老爷子最近一篇文章BigchangestoErlang掀起不

 bubuko.com,布布扣



     Erlang 增加
Maps数据类型并不是很突然,因为这个提议已经进行了2~3年之久,只不过Joe Armstrong老爷子最近一篇文章Big
changes to Erlang掀起不小了风浪.这篇文章用了比较夸张的说法:"Records are dead - long
live maps !",紧接着在国内国外社区这句话就传遍了.马上就有开发者忧心忡忡的在Stackoverflow上提问:Will
Erlang R17 still have records? 

 

 
 套用一句文艺的话,当我们谈论Maps时,实际上是表达我们对record的不满,这些不满/痛点恰好就是我们寄希望于Maps能够提供给我们的.本文将尽可能的逐一列出这些点,并尝试分析原因,下篇文章将深入分析Maps的一些细节.

 

 


Record的痛点


 

  使用Record我们遇到哪些痛点呢?这些痛点在Maps出现之后有所改善吗?我们先从细数痛点开始:

 

1.可以把record的name用作参数吗?


简单讲就是#RecordName{} 可以吗? 

 



?






1

2

3

4

5

6

7

8

9

10


7> rd(person,{name,id}).

person

8> #person{}.

#person{name = undefined,id = undefined}

9> P=person.

person

10> #P{}.

* 1: syntax error before: P

10>


  


Is it possible to use record name as a parameter in
Erlang
http://stackoverflow.com/questions/4103731/is-it-possible-to-use-record-name-as-a-parameter-in-erlang

 

 

2.可以把record的filed作为参数使用吗?

 





?






1

2

3

4

5


10> N=name.

name

11> #person{N="zen"}.

* 1: field ‘N‘
is not an atom or _ in
record person

12>


   


Modify a record in Erlang by programmatically specifying the
field to modify

http://stackoverflow.com/questions/13188449/modify-a-record-in-erlang-by-programmatically-specifying-the-field-to-modify/13188717#13188717

 

解决这个问题可以关注dynarec项目,可以动态生成record字段值的getter和setter访问入口. https://github.com/jcomellas/mlapi/blob/master/src/dynarec.erl

 

3. a.b.c.d.e.f 能实现吗?

 

在有些语言中会有Fluent API(或 Fluent
Interface)的设计,目的是在语法层面方便完成一系列连续的逻辑.在使用嵌套record的时候,我们特别希望能用a.b.c.d.e.f的方式来简化代码,而实际上是下面这个样子:

 





?






1

2

3

4

5

6

7

8

9

10

11

12

13

14

15


Eshell V6.0  (abort with ^G)

1> rd(foo,{a,b,c}).

foo

2>  rd(a,{f,m}).

a

3>  rd(f,{id,name}).

f

4>  #foo{a=#a{f=#f{id=2002,name="zen"},m=1984},b=1234,c=2465}.

#foo{a = #a{f = #f{id = 2002,name = "zen"},m = 1984},

     b = 1234,c = 2465}

5> D=v(4).

#foo{a = #a{f = #f{id = 2002,name = "zen"},m = 1984},

     b = 1234,c = 2465}

6> D#foo.a#a.f#f.name.

"zen"


  


有一个开源项目recbird就可以实现这种效果,解决的路子当然是parse_transform,
需要在代码中添加-compile({parse_transform, recbird}).选项

recbird的作者是dcaoyuan,这个代码也是作为ErlyBird的一部分host在sourceforge:

http://sourceforge.net/p/erlybird/code/HEAD/tree/trunk/erlybird/erlang-snippets/recbird.erl

 

 

4.record转proplists proplists转record

 

  为什么要转换properlist?其目的就是方便检索字段值.

 

这个之前讨论过 http://www.cnblogs.com/me-sa/archive/2012/05/22/erlang-code-snippet-2.html

record_info扩展项目 https://github.com/hio/erlang-record_info/blob/master/src/record_info.erl

 

5.key只能是atom

  的确有人提过这个 

 

原因何在?


 

   在record相关的问题中,常常提到的一个词就是"compile-time
dependency",即record只存在于编译时,并没有对应实际的数据类型.record本质上是tuple在语法层面的语法糖,而上面record的诸多问题其实就是源于tuple,在著名的exprecs项目,有这样一段描述:

 


This parse transform can be used to reduce compile-time
dependencies in large systems.

In the old days, before
records, Erlang programmers often wrote access functions for tuple
data. This was tedious and error-prone. The record syntax made
this easier, but since records were implemented fully in the
pre-processor, a nasty compile-time dependency was
introduced.

This module automates the generation of access
functions for records. While this method cannot fully replace the
utility of pattern matching, it does allow a fair bit of
functionality on records without the need for compile-time
dependencies.

 

 

 

Record即Tuple

 

  在内部表示没有record只有tuple, 下面是Erlang数据内部表示的介绍,我做了一个长图:


源文档地址:http://www.erlang-factory.com/upload/presentations/467/Halfword_EUC_2011.pdf 
(这个文档在我们的 Erlang
Resources 小站多次推荐过)

 

bubuko.com,布布扣

 

 

 

 

 这几张图可以帮助我们建立起来Erlang数据内部表示的思考模型,我们简单梳理一下:

   Beam(Bj?rns/Bogdans Erlang Abstract
Machine)虚拟机,包含一个拥有1024个虚拟寄存器的虚拟寄存器机,程序变量可能存储在register或stack;垃圾回收是以进程为单位,逐代进行;Beam包含一个常量池( constant
pool)不被GC.大型二进制数据在Heap外,并可被多个进程共享;VM
Code中用来表达数据类型使用的概念是Eterm:一个Eterm通常一个字(word)大小( sizeof(void
*)),进程的Heap实际上就是Eterm构成的数组,ETS也是以Eterm的形式存储数据.寄存器(register)也是Eterm,VM中的stack也是由Eterm组成;VM需要在进程heap上分配一些Eterm来表示一些复杂的数据结构比如list,tuple;如果变量指向的数据复杂,那么stack/register会包含指向heap的指针,换句话话说,Eterm要支持指针;



 Eterm其实是使用一些二进制数据位来标记当前的数据类型,Erlang使用了一个层次化的标记系统,最基础的是使用最低两位primary
tags来标识:

 00 = Continuation pointer (return
address on stack) or header word on heap

 01 = Cons cell (list)

 10 = Boxed (tuple, float, bignum, binary,
external pid/port, exterrnal/internal ref ...)

 11 =  Immediate (the rest - secondary
tag present)

 

具体到Boxed类型,继续细分:

– 0000 = Tuple
– 0001 = Binary match state (internal
type)
– 001x = Bignum (needs more than 28 bits)
– 0100 =
Ref
– 0101 = Fun
– 0110 = Float
– 0111 = Export fun
(make_fun/3)
– 1000 - 1010 = Binaries
– 1100 - 1110 = External
entities (Pids, Ports and Refs)

 

看到了吧,这里已经没有record的踪影了,只有tuple,而对于Maps,我们已经可以在17.0-rc2/erts/emulator/beam/erl_term.h的代码中找到它的subtag:

 

#define
ARITYVAL_SUBTAG         
(0x0 <<_TAG_PRIMARY_SIZE) /* TUPLE */
#define
BIN_MATCHSTATE_SUBTAG     (0x1 <<
_TAG_PRIMARY_SIZE)
#define
POS_BIG_SUBTAG         
(0x2 <<_TAG_PRIMARY_SIZE) /* BIG: tags 2&3 */
#define
NEG_BIG_SUBTAG         
(0x3 <<_TAG_PRIMARY_SIZE) /* BIG: tags 2&3 */
#define
_BIG_SIGN_BIT         
(0x1 <<_TAG_PRIMARY_SIZE)
#define
REF_SUBTAG         
(0x4 <<_TAG_PRIMARY_SIZE) /* REF */
#define
FUN_SUBTAG         
(0x5 <<_TAG_PRIMARY_SIZE) /* FUN */
#define
FLOAT_SUBTAG         
(0x6 <<_TAG_PRIMARY_SIZE) /* FLOAT */
#define
EXPORT_SUBTAG         
(0x7 <<_TAG_PRIMARY_SIZE) /* FLOAT */
#define
_BINARY_XXX_MASK     (0x3 <<
_TAG_PRIMARY_SIZE)
#define
REFC_BINARY_SUBTAG     (0x8 <<
_TAG_PRIMARY_SIZE) /* BINARY */
#define
HEAP_BINARY_SUBTAG     (0x9 <<
_TAG_PRIMARY_SIZE) /* BINARY */
#define
SUB_BINARY_SUBTAG     (0xA <<
_TAG_PRIMARY_SIZE) /* BINARY */
#define
MAP_SUBTAG         
(0xB <<_TAG_PRIMARY_SIZE) /* MAP */
#define
EXTERNAL_PID_SUBTAG     (0xC <<
_TAG_PRIMARY_SIZE) /* EXTERNAL_PID */
#define
EXTERNAL_PORT_SUBTAG     (0xD <<
_TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */
#define
EXTERNAL_REF_SUBTAG     (0xE <<
_TAG_PRIMARY_SIZE) /* EXTERNAL_REF */

 

 
感兴趣的话,可以继续在otp_src_17.0-rc2\erts\emulator\beam\erl_term.h中看到tuple实现相关的代码,搜索/*
tuple access methods */代码段.

 

  看到这里,Stackoverflow 有个问题讨论"Does
erlang implement record copy-and-modify in any clever
way?"

 

  注意里面提到的erts_debug:size/1 和
erts_debug:flat_size/1方法,可以帮助我们查看共享和非共享状态数据占用的字数.所谓的共享和非共享,就是通过复用一些数据块(即指针指向)而不是通过数据拷贝,这样提高效率.在一些万不得已的情况下再触发拷贝,比如数据发往别的节点,存入ETS等等, Erlang
Efficiency Guide 很多优化的小技巧都是从这个出发点考虑的.

 

 那去掉primary tag和sub
tag之后tuple是一个什么样的数据结构呢?我们可以从两个角度来看,首先是Erlang
Interface Reference Manual中

erl_mk_tuple方法明确指示了tuple实际上是一个Eterm的数组:

 

ETERM
*erl_mk_tuple(array, arrsize)


Types:


ETERM **array;


int arrsize;


Creates an Erlang tuple from an array of Erlang
terms.


array is an array of Erlang terms.


arrsize is the number of elements in array.

 

  另外一个角度就是在bif.c中,tuple_to_list和list_to_tuple的实现,其实就是数组和链表的互相转换,看代码还可以知道通过make_arityval(len)冗余了数组的长度.对于tuple,获得size和按照索引访问数据都是很快的.而编译期一过,record提供的语法红利没有了,剩下的也就是快速获得tuple
size和按照索引访问数据了.exprecs项目所谓 reduce compile-time dependencies
其实就是在编译阶段把一些语法红利继续保持下去,比如可以按照record
name去new一个record,按照字段索引位置访问数据等等.上面提到的record与proplists的转换,实际上是把解决问题的时机从编译期推迟到了运行时.

 

 

说到这里,你可能非常期待了,Erlang
R17之后加入的Maps又解决了什么问题?带来了什么惊喜呢?Maps与Record是一场你死我活的PK么?我们明天再说,敬请关注.

 

 

PS. Joe Armstrong老爷子文章中提到的Names in Funs 之前我们已经讨论过多次了:

 

[Erlang 0050]用fun在Erlang Shell中编写尾递归
http://www.cnblogs.com/me-sa/archive/2012/03/24/you-win-yourself-zen-this-is-the-50-erlang-article-go-on.html


[Erlang 0056] 用fun在Erlang Shell中编写尾递归 Ⅱ
http://www.cnblogs.com/me-sa/archive/2012/04/28/2474892.html


[Erlang 0063] Joe Armstrong 《A Few Improvements to
Erlang》EUC 2012
http://www.cnblogs.com/me-sa/archive/2012/06/06/2538941.html

 

 

相关资料:

 

[0] setelement/3 优化http://www.erlang.org/doc/efficiency_guide/commoncaveats.html#id62422

[1] http://www.erlang.org/doc/man/erl_eterm.html

[2] http://erlang.org/doc/efficiency_guide/users_guide.html

 

[Erlang 0116] 当我们谈论Erlang Maps时,我们谈论什么 Part 1,布布扣,bubuko.com


推荐阅读
  • 本文介绍了在Ubuntu 11.10 x64环境下安装Android开发环境的步骤,并提供了解决常见问题的方法。其中包括安装Eclipse的ADT插件、解决缺少GEF插件的问题以及解决无法找到'userdata.img'文件的问题。此外,还提供了相关插件和系统镜像的下载链接。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 本文整理了Java中com.evernote.android.job.JobRequest.getTransientExtras()方法的一些代码示例,展示了 ... [详细]
  • 如何使用PLEX播放组播、抓取信号源以及设置路由器
    本文介绍了如何使用PLEX播放组播、抓取信号源以及设置路由器。通过使用xTeve软件和M3U源,用户可以在PLEX上实现直播功能,并且可以自动匹配EPG信息和定时录制节目。同时,本文还提供了从华为itv盒子提取组播地址的方法以及如何在ASUS固件路由器上设置IPTV。在使用PLEX之前,建议先使用VLC测试是否可以正常播放UDPXY转发的iptv流。最后,本文还介绍了docker版xTeve的设置方法。 ... [详细]
  • Harmony 与 Game Space 达成合作,在 Shard1 上扩展 Web3 游戏
    旧金山20 ... [详细]
  • 解决Sharepoint 2013运行状况分析出现的“一个或多个服务器未响应”问题的方法
    本文介绍了解决Sharepoint 2013运行状况分析中出现的“一个或多个服务器未响应”问题的方法。对于有高要求的客户来说,系统检测问题的存在是不可接受的。文章详细描述了解决该问题的步骤,包括删除服务器、处理分布式缓存留下的记录以及使用代码等方法。同时还提供了相关关键词和错误提示信息,以帮助读者更好地理解和解决该问题。 ... [详细]
  • 原文地址http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/最开始时 ... [详细]
author-avatar
mobiledu2502923581
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有