【发布时间】:2011-06-24 15:17:40
【问题描述】:
例如我有erlang记录:
-record(state, {clients
}).
我可以从客户字段列表中制作吗?
我可以像在普通列表中一样保留在客户归档中吗?我怎样才能在这个列表中添加一些值?
谢谢。
【问题讨论】:
例如我有erlang记录:
-record(state, {clients
}).
我可以从客户字段列表中制作吗?
我可以像在普通列表中一样保留在客户归档中吗?我怎样才能在这个列表中添加一些值?
谢谢。
【问题讨论】:
也许你的意思是这样的:
-module(reclist).
-export([empty_state/0, some_state/0,
add_client/1, del_client/1,
get_clients/1]).
-record(state,
{
clients = [] ::[pos_integer()],
dbname ::char()
}).
empty_state() ->
#state{}.
some_state() ->
#state{
clients = [1,2,3],
dbname = "QA"}.
del_client(Client) ->
S = some_state(),
C = S#state.clients,
S#state{clients = lists:delete(Client, C)}.
add_client(Client) ->
S = some_state(),
C = S#state.clients,
S#state{clients = [Client|C]}.
get_clients(#state{clients = C, dbname = _D}) ->
C.
测试:
1> reclist:empty_state().
{state,[],undefined}
2> reclist:some_state().
{state,[1,2,3],"QA"}
3> reclist:add_client(4).
{state,[4,1,2,3],"QA"}
4> reclist:del_client(2).
{state,[1,3],"QA"}
::[pos_integer()]表示该字段的类型是一个正整数值的列表,从1开始;这是分析工具dialyzer在执行类型检查时的提示。
Erlang 还允许您在记录上使用模式匹配:
5> reclist:get_clients(reclist:some_state()).
[1,2,3]
进一步阅读:
@JUST 我的正确意见answer 让我记得我喜欢 Haskell 如何获取数据类型中字段的值。
这是从Learn You a Haskell for Great Good! 窃取的数据类型的定义,它利用了记录语法:
data Car = Car {company :: String
,model :: String
,year :: Int
} deriving (Show)
它创建函数company、model 和year,在数据类型中查找字段。我们先造一辆新车:
ghci> Car "Toyota" "Supra" 2005
Car {company = "Toyota", model = "Supra", year = 2005}
或者,使用记录语法(字段顺序无关紧要):
ghci> Car {model = "Supra", year = 2005, company = "Toyota"}
Car {company = "Toyota", model = "Supra", year = 2005}
ghci> let supra = Car {model = "Supra", year = 2005, company = "Toyota"}
ghci> year supra
2005
我们甚至可以使用模式匹配:
ghci> let (Car {company = c, model = m, year = y}) = supra
ghci> "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
"This Toyota Supra was made in 2005"
我记得有人尝试在 Erlang 中实现类似于 Haskell 的记录语法的东西,但不确定它们是否成功。
关于这些尝试的一些帖子:
当您想要创建某个结构的新值时,LFE 似乎使用了类似于提供 Scheme 的宏(例如Racket):
> (define-struct car (company model year))
> (define supra (make-car "Toyota" "Supra" 2005))
> (car-model supra)
"Supra"
我希望我们将来会有一些接近 Haskell 记录语法的东西,这将非常实用和方便。
【讨论】:
dialyzer。更多关于 Erlang Records.
clients = [] ::[pos_integer()],
如果您只是在该州的客户列表中添加或删除单个项目,您可以减少使用宏输入。
-record(state, {clients = [] }).
-define(AddClientToState(Client,State),
State#state{clients = lists:append([Client], State#state.clients) } ).
-define(RemoveClientFromState(Client,State),
State#state{clients = lists:delete(Client, State#state.clients) } ).
这是一个演示的测试脚本:
#!/usr/bin/env escript
-record(state, {clients = [] }).
-define(AddClientToState(Client,State),
State#state{clients = lists:append([Client], State#state.clients)} ).
-define(RemoveClientFromState(Client,State),
State#state{clients = lists:delete(Client, State#state.clients)} ).
main(_) ->
%Start with a state with a empty list of clients.
State0 = #state{},
io:format("Empty State: ~p~n",[State0]),
%Add foo to the list
State1 = ?AddClientToState(foo,State0),
io:format("State after adding foo: ~p~n",[State1]),
%Add bar to the list.
State2 = ?AddClientToState(bar,State1),
io:format("State after adding bar: ~p~n",[State2]),
%Add baz to the list.
State3 = ?AddClientToState(baz,State2),
io:format("State after adding baz: ~p~n",[State3]),
%Remove bar from the list.
State4 = ?RemoveClientFromState(bar,State3),
io:format("State after removing bar: ~p~n",[State4]).
结果:
Empty State: {state,[]}
State after adding foo: {state,[foo]}
State after adding bar: {state,[bar,foo]}
State after adding baz: {state,[baz,bar,foo]}
State after removing bar: {state,[baz,foo]}
【讨论】:
Yasir's answer 是正确的,但我将向您展示它为何如此运作,以便您更好地理解记录。
Erlang 中的记录是一种 hack(而且非常丑陋)。使用 Yasir 回答中的记录定义...
-record(state,
{
clients = [] ::[pos_integer()],
dbname ::char()
}).
...当您使用 #state{} 实例化它时(就像 Yasir 在 empty_state/0 函数中所做的那样),您真正得到的是:
{state, [], undefined}
也就是说,您的“记录”只是一个标有记录名称的元组(在这种情况下为state),后跟记录的内容。在 BEAM 内部没有记录。它只是另一个包含 Erlang 数据类型的元组。这是了解事物如何工作的关键(以及启动记录的限制)。
现在当 Yasir 这样做时......
add_client(Client) ->
S = some_state(),
C = S#state.clients,
S#state{clients = [Client|C]}.
...S#state.clients 位在内部转换为类似于element(2,S) 的代码。换句话说,您正在使用标准的元组操作函数。 S#state.clients 只是表示同一件事的一种象征性方式,但它让您知道元素 2 实际上是。与以容易出错的方式跟踪元组中的各个字段相比,语法糖精是一种改进。
现在对于最后一个S#state{clients = [Client|C]} 位,我对幕后生成的代码不是绝对肯定的,但它可能只是与{state, [Client|C], element(3,S)} 等效的简单内容。它:
#state),S 复制元素(由S# 部分决定),{clients = [Client|C]}覆盖的clients部分。所有这些魔法都是通过幕后的预处理技巧完成的。
了解记录在幕后的工作方式对于理解使用记录编写的代码以及了解如何自己使用它们都是有益的(更不用说理解为什么看起来“有意义”的事情不适用于记录 - - 因为它们实际上并不存在于抽象机器中……但)。
【讨论】: