Lisp和Erlangprimefaces,Ruby和Scheme符​​号。 他们有多好?

在编程语言中使用primefaces数据types的function有多有用?

一些编程语言有primefaces或符号的概念来表示一个常数的种类。 我遇到的语言(Lisp,Ruby和Erlang)有一些差异,但是在我看来,一般的概念是相同的。 我对编程语言devise很感兴趣,我想知道在现实生活中提供primefacestypes的价值。 其他语言如Python,Java,C#在没有它的情况下似乎做得相当好。

我没有真正的Lisp或Ruby经验(我知道的语法,但没有在一个真实的项目中使用)。 我已经使用Erlang足够用于这个概念了。

一个简短的例子,显示如何操纵符号的能力导致更干净的代码:(代码是在scheme,一个Lisp的方言)。

(define men '(socrates plato aristotle)) (define (man? x) (contains? men x)) (define (mortal? x) (man? x)) ;; test > (mortal? 'socrates) => #t 

你可以使用string或整数常量来编写这个程序。 但是符号版本有一定的优势。 一个符号保证在系统中是唯一的。 这使得比较两个符号的速度与比较两个指针的速度一样快。 这显然比比较两个string更快。 使用整数常量允许人们写下无意义的代码,如:

 (define SOCRATES 1) ;; ... (mortal? SOCRATES) (mortal? -1) ;; ?? 

这个问题的详细答案可以在“ Common Lisp:象征性计算的简单介绍 ”一书中find。

primefaces是文字,常量有自己的名字为价值。 你看到的是你所得到的,而不是期待更多。 primefaces猫的意思是“猫”,就是这样。 你不能玩它,你不能改变它,你不能粉碎它; 它是猫。 处理它。

我将primefaces与其名称作为其值的常量进行了比较。 你可能曾经使用过使用常量的代码:例如,假设我有眼睛颜色的值: BLUE -> 1, BROWN -> 2, GREEN -> 3, OTHER -> 4 。 您需要将常量的名称与某个基础值相匹配。 primefaces让你忘记了底层的价值:我的眼睛颜色可以简单地是“蓝色”,“棕色”,“绿色”和“其他”。 这些颜色可以在任何代码的任何地方使用:底层的值永远不会冲突,这样的常量是不可能被定义的!

取自http://learnyousomeerlang.com/starting-out-for-real#atoms

这就是说,primefaces最终是一个更好的语义适合于描述你的代码中的数据在其他语言将被迫使用string,枚举或定义的地方。 他们更安全,更友好地用于类似的预期结果。

如果语义值没有自然的基础“本地”表示,primefaces(在Erlang或Prolog等中)或符号(在Lisp或Ruby等中) – 从此处仅称为primefaces – 非常有用。 他们采取这样的C型枚举的空间:

 enum days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } 

不同之处在于,primefaces通常不需要声明,而且也没有任何底层expression方式需要担心。 Erlang或Prolog中的primefacesmonday具有“primefacesmonday ”的值,不多也不less。

虽然确实可以从stringtypes中获得与primefaces不同的用法,但后者有一些优点。 首先,由于primefaces保证是唯一的(在幕后,它们的string表示被转换成某种forms的易于testing的ID),所以比较它们比比较等效string要快得多。 其次,它们是不可分割的。 primefacesmonday不能testing,看它是否在day结束,例如。 它是一个纯粹的,不可分割的语义单位。 换句话说,你的概念重载要比string表示更less。

你也可以用C样式枚举获得很多相同的好处。 特别是比较速度,如果有的话,更快。 但是…这是一个整数。 而且你可以做奇怪的事情,比如SATURDAYSUNDAY翻译成同样的价值:

 enum days { SATURDAY, SUNDAY = 0, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY } 

这意味着你不能相信不同的“符号”(枚举)是不同的东西,从而使代码的推理更加困难。 而且,通过有线协议发送枚举types是有问题的,因为没有办法区分它们和常规整数。 primefaces没有这个问题。 primefaces不是一个整数,在幕后永远不会看起来像一个。

作为一名C程序员,我对于理解Ruby符号的真正含义存在一个问题。 当我看到源代码中的符号是如何实现的时候,我已经开悟了。

在Ruby代码里面,有一个全局哈希表,即映射到整数的string。 所有的ruby符号都保存在那里。 Ruby解释器,在源代码parsing阶段,使用该哈希表将所有符号转换为整数。 然后在内部所有的符号被视为整数。 这意味着一个符号只占用4个字节的内存,所有的比较都非常快。

所以基本上你可以把Ruby符号当作是以非常聪明的方式实现的string。 他们看起来像string,但performance几乎像整数。

当一个新的string被创build,然后在Ruby中分配一个新的C结构来保存该对象。 对于两个Rubystring,有两个指针指向两个不同的内存位置(可能包含相同的string)。 但是一个符号立即转换为C inttypes。 因此无法将两个符号区分为两个不同的Ruby对象。 这是实施的副作用 。 只需在编码时记住这一点,就这些了。

在Lisp中, 符号primefaces是两个不同的和不相关的概念。

通常在Lisp中,ATOM不是特定的数据types。 这是NOT CONS的简称。

 (defun atom (item) (not (consp item))) 

此外,ATOMtypes与types(NOT CONS)相同。

任何不是cons cell的东西都是Common Lisp中的一个primefaces。

SYMBOL是特定的数据types。

符号是具有名称和标识的对象。 一个符号可以被封装在一个包里 。 一个符号可以有一个值,一个函数和一个属性列表。

 CL-USER 49 > (describe 'FOO) FOO is a SYMBOL NAME "FOO" VALUE #<unbound value> FUNCTION #<unbound function> PLIST NIL PACKAGE #<The COMMON-LISP-USER package, 91/256 internal, 0/4 external> 

在Lisp源代码中,variables,函数,类等的标识符被写为符号。 如果一个Lisp sexpression式被读者读取,它会创build新的符号,如果它们不是已知的(在当前包中可用),或者重用已有的符号(如果它在当前包中可用的话。列表像

 (snow snow) 

那么它会创build一个两个cons单元的列表。 每个利弊细胞的CAR指向相同的符号 。 Lisp内存中只有一个符号。

还要注意,符号的plist(属性列表)可以存储符号的附加元信息。 这可以是作者,源位置等。用户也可以在他/她的程序中使用这个特征。

在Scheme(以及Lisp家族的其他成员)中,符号不仅仅是有用的,它们是必不可less的。

这些语言的一个有趣的属性是它们是同心的 。 Scheme程序或expression式本身可以表示为有效的Scheme数据结构。

一个例子可能会使这个更清楚(使用Gauche计划):

 > (define x 3) x > (define expr '(+ x 1)) expr > expr (+ x 1) > (eval expr #t) 4 

在这里, expr只是一个列表,由符号+ ,符号x和数字1组成 。 我们可以像任何其他的一样操纵这个列表,传递它,等等。但是我们也可以评估它,在这种情况下,它将被解释为代码。

为了使这个工作,计划需要能够区分符号和string文字。 在上面的例子中, x是一个符号。 它不能被string文字replace而不改变意义。 如果我们接受一个列表(print x) ,其中x是一个符号,并对它进行评估,那么这意味着不是“(print”x“) ,其中”x“是一个string。

顺便说一下,使用Scheme数据结构表示Schemeexpression式的能力不仅仅是一个噱头, 将expression式作为数据结构读取并以某种方式进行转换是macros的基础。

在一些语言中,关联数组文字具有像符号一样的键。

在Python [1]中,是一个字典。

 d = dict(foo=1, bar=2) 

在Perl [2]中,哈希。

 my %h = (foo => 1, bar => 2); 

在JavaScript [3]中,一个对象。

 var o = {foo: 1, bar: 2}; 

在这些情况下, foobar就像符号一样,即不带引号的不可变string。

[1]certificate:

 x = dict(a=1) y = dict(a=2) (k1,) = x.keys() (k2,) = y.keys() assert id(k1) == id(k2) 

[2]这不是真的:

 my %x = (a=>1); my %y = (a=>2); my ($k1) = keys %x; my ($k2) = keys %y; die unless \$k1 == \$k2; # dies 

[1]在JSON中,这个语法是不允许的,因为键必须被引用。 我不知道如何certificate它们是符号,因为我不知道如何读取variables的内存。

实际上你说python与primefaces或符号没有任何相似之处。 使python中的物体具有类似primefaces的物体并不困难。 只要制造好的物体。 纯粹的空物体。 例:

 >>> red = object() >>> blue = object() >>> c = blue >>> c == red False >>> c == blue True >>> 

TADA! Python中的primefaces! 我总是使用这个技巧。 其实,你可以走得更远。 你可以给这些对象一个types:

 >>> class Colour: ... pass ... >>> red = Colour() >>> blue = Colour() >>> c = blue >>> c == red False >>> c == blue True >>> 

现在,你的颜色有一个types,所以你可以做这样的东西:

 >>> type(red) == Colour True >>> 

如果你问我,这实际上是对lispy符号的改进

相对于浮点常数值,primefaces保证是唯一的和不可分割的,它们在编码时会因为不准确而不同,通过电线发送,在另一端解码并转换回浮点。 无论您使用何种版本的解释器,它都能确保primefaces总是具有相同的“价值”并且是唯一的。

Erlang VM将所有模块中定义的所有primefaces存储在全局primefaces表中 。

Erlang中没有布尔数据types 。 相反,primefacesfalse用来表示布尔值。 这可以防止人们做这样的讨厌的事情:

 #define TRUE FALSE //Happy debugging suckers 

在Erlang中,您可以将primefaces保存到文件中,然后读取它们,通过远程Erlang虚拟机之间的线路传递它们。

举个例子,我将一些术语保存到一个文件中,然后再读回来。 这是Erlang源文件lib_misc.erl (或者现在我们最感兴趣的部分):

 -module(lib_misc). -export([unconsult/2, consult/1]). unconsult(File, L) -> {ok, S} = file:open(File, write), lists:foreach(fun(X) -> io:format(S, "~p.~n",[X]) end, L), file:close(S). consult(File) -> case file:open(File, read) of {ok, S} -> Val = consult1(S), file:close(S), {ok, Val}; {error, Why} -> {error, Why} end. consult1(S) -> case io:read(S, '') of {ok, Term} -> [Term|consult1(S)]; eof -> []; Error -> Error end. 

现在我将编译这个模块,并将一些术语保存到一个文件中:

 1> c(lib_misc). {ok,lib_misc} 2> lib_misc:unconsult("./erlang.terms", [42, "moo", erlang_atom]). ok 3> 

erlang.terms文件中,我们会得到这个内容:

 42. "moo". erlang_atom. 

现在让我们读回来:

 3> {ok, [_, _, SomeAtom]} = lib_misc:consult("./erlang.terms"). {ok,[42,"moo",erlang_atom]} 4> is_atom(SomeAtom). true 5> 

您会看到数据已成功从文件中读取,并且variablesSomeAtom确实包含primefaceserlang_atom


lib_misc.erl内容摘自Joe Armstrong的“编程Erlang:并发世界的软件”,The Pragmatic Bookshelf出版。 其余的源代码在这里 。

primefaces提供快速的平等testing,因为他们使用身份。 与枚举types或整数相比,它们具有更好的语义(为什么用一个数字表示一个抽象的符号值?),它们并不局限于像枚举这样的固定值。

妥协是因为系统需要知道所有存在的实例来保持唯一性,所以它们比string更昂贵。 这大部分花费时间用于编译器,但是它花费O(独特primefaces的数量)的内存。

在Ruby中,符号通常用作哈希中的键,所以Ruby 1.9甚至引入了构造哈希的简写。 你以前写的是:

 {:color => :blue, :age => 32} 

现在可以写成:

 {color: :blue, age: 32} 

本质上,它们是string和整数之间的东西:在源代码中,它们类似于string,但具有相当大的差异。 相同的两个string实际上是不同的实例,而相同的符号总是相同的实例:

 > 'foo'.object_id # => 82447904 > 'foo'.object_id # => 82432826 > :foo.object_id # => 276648 > :foo.object_id # => 276648 

这对性能和内存消耗都有影响。 而且,它们是不变的。 分配后不打算改动一次。

一个有争议的经验法则是使用符号而不是string为每个string不意味着输出。

虽然也许看起来毫不相关,但大多数代码高亮编辑器的颜色符号与其他代码不同,使得视觉上的区别。

我在其他语言(如C)中使用类似概念的问题可以很容易地expression为:

 #define RED 1 #define BLUE 2 #define BIG 1 #define SMALL 2 

要么

 enum colors { RED, BLUE }; enum sizes { BIG, SMALL }; 

造成如下问题的原因:

 if (RED == BIG) printf("True"); if (BLUE == 2) printf("True"); 

这两者都没有任何意义。 primefaces解决了类似的问题,没有上面提到的缺点。

primefaces就像一个开放的枚举,具有无限可能的价值,不需要事先声明任何东西。 这就是他们通常在实践中使用的方式。

例如,在Erlang中,一个进程希望接收一些消息types之一,并且用primefaces标记消息是最方便的。 大多数其他的语言会使用一个枚举的消息types,这意味着每当我想发送一个新types的消息,我必须去添加到声明。

而且,与枚举不同,可以组合一组primefaces值。 假设我想监视我的Erlang进程的状态,并且我有一些标准的状态监视工具。 我可以扩展我的过程以响应状态消息协议以及其他消息types 。 用枚举,我将如何解决这个问题?

 enum my_messages { MSG_1, MSG_2, MSG_3 }; enum status_messages { STATUS_HEARTBEAT, STATUS_LOAD }; 

问题是MSG_1是0,STATUS_HEARTBEAT也是0.当我得到一个types为0的消息时,它是什么? 用primefaces,我没有这个问题。

primefaces/符号不只是与恒定时间比较的string:)。