Elixir有大量可用的数据类型。 常用的基本类型integer
, float
, boolean
和string
在这里,但是atom
/ symbol, list
, tuple
和anonymous functions
。 您将在本教程中了解所有有关它们的信息。
开始之前:我们将在Elixir的交互模式下运行这些示例。 在终端中输入iex
进入交互模式。 有关更多信息,请阅读本指南的第1部分 。 ( Windows用户运行iex.bat --werl
。)
前言
Elixir的构建考虑了元编程。 所有这些都是基于宏构建的。 如果您不熟悉宏,它们只是执行特定任务的一条指令 。 也许不太困难,但是这里有一些真实的例子:
if worked? do
IO.puts("You just used a macro")
end
if :name? do
IO.puts(:name)
end
if
这里是我们众所周知的标准条件if ...结构的宏。 Elixir将在内部为您编译宏的结果。
元编程?
Elixir的基础是引用表达式的操纵。
元编程本质上意味着您可以从代码创建代码(程序实际上具有将自己的代码视为数据的能力……确实是一个巨大的步骤。)
Elixir程序可以表示为自己的数据结构。
例如,Elixir的构建基块由具有三个元素的tuple
表示(稍后将在tuples
详细介绍)。 函数调用sum(1,2,3)
的定义如下:
{:sum, [], [1, 2, 3]}
您可以通过使用quote
宏来检索任何宏的表示形式:
iex> quote do: sum(1, 2, 3)
{:sum, [], [1, 2, 3]}
注意:除quote
宏之外,还有unquote
宏-您可以在Elixir文档中阅读有关该主题的更多信息 。
因此,我们看到第一个元素是:sum
的函数名称,第二个元素是包含元数据(当前示例中为空白)的keyword list
(稍后会详细介绍),第三个是参数列表。
所有宏都是使用这些数据结构构建的,因此这意味着我们的代码可以具有修改和重新编译自己的代码的固有能力。 因此,您的应用程序现在可以编写自己的代码,检查代码中的错误并进行修复,甚至可以扫描添加的插件并检查其中的代码并即时对其进行修改。
人工智能的意义显然是巨大的,但首先,在本指南中,我们必须继续进行基础研究,并在开始更深入地研究之前,对Elixir使用的各种数据类型有牢固的基础。
基本类型
现在您已经在交互式控制台中,让我们看一下Booleans
。
iex> true
true
iex> true == false
false
支持true
和false
的标准行为。 要检查该值,我们使用Elixir提供的函数is_boolean
。
iex> is_boolean(true)
true
iex> is_boolean(1)
false
每种类型还具有这些谓词功能之一。 例如, is_integer/1
, is_float/1
或is_number/1
全部将检查参数是否为整数,浮点数或两者之一。
Elixir始终以这种方式引用函数,在斜杠后面加一个数字以表示该函数采用的参数数量。 因此is_boolean/1
需要1个参数,例如is_boolean(1)
。
注意 :如果您想随时在交互式Shell中访问帮助,只需键入h
,您就可以访问有关如何使用Elixir Shell的信息。 另外,您可以使用h is_integer/1
来接收有关is_integer/1
文档,从而找到有关Elixir的任何运算符或函数的信息。
在数学上,我们还可以执行以下功能:
iex> round(3.58)
4
iex> trunc(3.58)
3
在Elixir中,当对integer
执行除法integer
,返回始终为float
:
iex> 5 / 2
2.5
iex> 10 / 2
5.0
如果我们想获得除法的余数或进行整数除法,可以这样使用div/2
和rem/2
函数:
iex> div(10, 2)
5
iex> div 10, 2
5
iex> rem 10, 3
1
注意:函数调用不需要括号。
您也可以只将任何binary
, octal
或hexadecimal
数字输入到iex
。
iex> 0b1010
10
iex> 0o777
511
iex> 0x1F
31
原子(符号)
这些就像constants
,但它们的名称是它们自己的值。 一些语言将此称为符号。 布尔true
和false
也是符号的示例。
iex> :hello
:hello
iex> :hello == :world
false
元组
与lists
类似,并由花括号定义,它们可以包含任何数据,如下所示:
iex> {:ok, "hello"}
{:ok, "hello"}
iex> tuple_size {:ok, "hello"}
2
清单
与tuples
类似,您可以定义一个带有方括号的list
,如下所示:
iex> [1, 2, true, 3]
[1, 2, true, 3]
iex> length [1, 2, 3]
3
可以串联和减去两个列表:
iex> [1, 2, 3] ++ [4, 5, 6]
[1, 2, 3, 4, 5, 6]
iex> [1, true, 2, false, 3, true] -- [true, false]
[1, 2, 3, true]
要返回列表的开头或列表的末尾,我们使用hd
和tl
函数(head和tail的缩写)。
列表和元组之间有什么区别?
Lists
以链接列表的形式存储在内存中,链接列表是在线性操作中迭代和访问的值对列表。 因此,只要我们将其追加到list
,更新就会很快,但是如果我们在list
进行修改,则操作会较慢。
另一方面, Tuples
连续存储在内存中。 这意味着获取元组的总大小或仅访问一个元素很快。 但是在这里,与列表相比,追加到现有tuple
的速度很慢,并且需要在内存中复制整个tuple
。
匿名函数
我们可以使用fn
和end
关键字定义一个函数。
iex> myFunc = fn a, b -> a + b end
#Function<12.71889879/2 in :erl_eval.expr/5>
iex> is_function(myFunc)
true
iex> is_function(myFunc, 2)
true
iex> is_function(myFunc, 1)
false
iex> myFunc.(1, 2)
3
Elixir的手册将匿名功能称为“头等公民”。 这意味着您可以像整数或字符串一样将参数传递给其他函数。
因此,在我们的示例中,我们已将变量myFunc
的匿名函数声明为is_function(myFunc)
检查函数,该函数正确返回了true
。 这意味着我们已经成功创建了我们的功能。
我们还可以通过调用is_function(myFunc, 2)
来检查myFunc
函数的参数数量。
注意:要调用匿名函数,必须在变量和括号之间使用点( .
)。
弦乐
我们在Elixir中的双引号之间定义字符串:
iex> "yo"
"yo"
#
符号也支持插值:
iex> "Mr #{:Smith}"
"Mr Smith"
对于串联,请使用<>
。
iex> "foo" <> "bar"
"foobar"
要获取字符串的长度,请使用String.length
函数:
iex> String.length("yo")
2
要基于模式拆分字符串,可以使用String.split
方法:
iex> String.split("foo bar", " ")
["foo", "bar"]
iex> String.split("foo bar!", [" ", "!"])
["foo", "bar", ""]
关键字清单
通常在编程中,可以创建两个数据项(本质上是两个tuples
的键值对,就像这样,其中键是atom
:
iex> list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]
iex> list == [a: 1, b: 2]
true
Elixir具有将列表定义为[key: value]
的语法。 然后,我们可以使用Elixir中可用的任何其他运算符(例如++
追加到列表的后面或前面。
iex> list ++ [c: 3]
[a: 1, b: 2, c: 3]
iex> [a: 0] ++ list
[a: 0, a: 1, b: 2]
关键字列表要记住三个要点:
- 键必须是
atoms
。 - 密钥是按开发人员指定的顺序排列的。
- 可以多次给出密钥。
对于数据库查询, Ecto库在执行查询时会使用此方法,如下所示:
query = from w in Weather,
where: w.prcp > 0,
where: w.temp < 20,
select: w
Elixir的if
宏也将其纳入其设计:
iex> if(false, [{:do, :this}, {:else, :that}])
:that
通常,当列表是函数的参数时,方括号是可选的。
地图
与关键字列表类似, maps
可以帮助您满足键值对的需求。 它们是通过%{}
语法创建的,如下所示:
iex> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex> map[:a]
1
iex> map[2]
:b
iex> map[:c]
nil
它们类似于关键字列表,但不完全相同-有两个区别:
- 映射允许将任何值用作键。
- 地图的键不遵循任何顺序。
注意:将map
所有键设置为atoms
,关键字语法非常方便:
iex> map = %{a: 1, b: 2}
%{a: 1, b: 2}
匹配
与关键字列表不同,地图非常适合模式匹配。 当将map
用于模式时,它将始终与给定值的子集匹配。
iex> %{} = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex> %{:a => a} = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex> a
1
iex> %{:c => c} = %{:a => 1, 2 => :b}
** (MatchError) no match of right hand side value: %{2 => :b, :a => 1}
只要提供的地图中存在键,模式就将始终匹配。 因此,为了匹配所有内容,我们使用一个空的地图。
为了使地图更进一步, 地图模块提供了强大的API来操作地图:
iex> Map.get(%{:a => 1, 2 => :b}, :a)
1
iex> Map.to_list(%{:a => 1, 2 => :b})
[{2, :b}, {:a, 1}]
嵌套数据结构
数据通常需要层次结构和结构。 药剂提供支持maps
内maps
,或keyword lists
内maps
等。 使用put_in
和update_in
宏可以很方便地进行操作,它们可以按如下方式使用:
假设我们有以下内容:
iex> users = [
john: %{name: "John", age: 27, languages: ["Erlang", "Ruby", "Elixir"]},
mary: %{name: "Mary", age: 29, languages: ["Elixir", "F#", "Clojure"]}
]
[john: %{age: 27, languages: ["Erlang", "Ruby", "Elixir"], name: "John"},
mary: %{age: 29, languages: ["Elixir", "F#", "Clojure"], name: "Mary"}]
因此,现在我们在表上有一些数据可以使用,每个用户的关键字列表都有一个包含名称,年龄和其他信息的地图。 如果要访问约翰的年龄,可以执行以下操作:
iex> users[:john].age 27
碰巧我们也可以使用以下相同的语法来更新值:
iex> users = put_in users[:john].age, 31
[john: %{age: 31, languages: ["Erlang", "Ruby", "Elixir"], name: "John"},
mary: %{age: 29, languages: ["Elixir", "F#", "Clojure"], name: "Mary"}]
正则表达式
Elixir使用基于PCRE的Erlang的:re
模块。 您可以在文档中找到更多信息。
为了创建在药剂正则表达式,使用Regex.compile
法或特殊速记形式~r
或~R
# A simple regular expression that matches foo anywhere in the string
~r/foo/
# A regular expression with case insensitive and Unicode options
~r/foo/iu
Regex模块具有大量有用的函数,可用于验证正则表达式,对其进行编译以及正确地进行转义。 这里有些例子:
iex> Regex.compile("foo")
{:ok, ~r"foo"}
iex> Regex.compile("*foo")
{:error, {'nothing to repeat', 0}}
iex> Regex.escape(".")
"\\."
iex> Regex.escape("\\what if")
"\\\\what\\ if"
iex> Regex.replace(~r/d/, "abc", "d")
"abc"
iex> Regex.replace(~r/b/, "abc", "d")
"adc"
iex> Regex.replace(~r/b/, "abc", "[\\0]")
"a[b]c"
iex> Regex.match?(~r/foo/, "foo")
true
您还可以在map
运行正则表达式,并使用named_captures
方法检索结果:
iex> Regex.named_captures(~r/c(?d)/, "abcd")
%{"foo" => "d"}
iex> Regex.named_captures(~r/a(?b)c(?d)/, "abcd")
%{"bar" => "d", "foo" => "b"}
iex> Regex.named_captures(~r/a(?b)c(?d)/, "efgh")
nil
结论
Elixir是一个完全基于宏用法的功能齐全的元编程环境。 当它与元编程功能结合使用时,它强大的lists
, maps
和anonymous functions
可以帮助您将开发人员带入新的应用程序设计和数据结构水平。
我们可以看看我们的方法中的特定解决方案,由于采用DSL (特定领域语言)和Elixir的形式为Elixir提供了强大的财富,因此在非宏语言中可能需要创建许多行或代码类来创建。 Erlang的模块。
密钥对值存储操作, list
和基本数据操作只是提供给开发人员的全部内容的一小部分。 我们将在本系列的下一部分继续介绍所有这些细节。
翻译自: https://code.tutsplus.com/tutorials/elixir-walkthrough-part-2-data-types--cms-27510