Erlang和Elixir,第2部分:数据类型

Elixir有大量可用的数据类型。 常用的基本类型integerfloatbooleanstring在这里,但是atom / symbol, listtupleanonymous 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

支持truefalse的标准行为。 要检查该值,我们使用Elixir提供的函数is_boolean

iex> is_boolean(true)
true
iex> is_boolean(1)
false

每种类型还具有这些谓词功能之一。 例如, is_integer/1is_float/1is_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/2rem/2函数:

iex> div(10, 2)
5
iex> div 10, 2
5
iex> rem 10, 3
1

注意:函数调用不需要括号。

您也可以只将任何binaryoctalhexadecimal数字输入到iex

iex> 0b1010
10
iex> 0o777
511
iex> 0x1F
31

原子(符号)

这些就像constants ,但它们的名称是它们自己的值。 一些语言将此称为符号。 布尔truefalse也是符号的示例。

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]

要返回列表的开头或列表的末尾,我们使用hdtl函数(head和tail的缩写)。

列表和元组之间有什么区别?

Lists以链接列表的形式存储在内存中,链接列表是在线性操作中迭代和访问的值对列表。 因此,只要我们将其追加到list ,更新就会很快,但是如果我们在list进行修改,则操作会较慢。

另一方面, Tuples连续存储在内存中。 这意味着获取元组的总大小或仅访问一个元素很快。 但是在这里,与列表相比,追加到现有tuple的速度很慢,并且需要在内存中复制整个tuple

匿名函数

我们可以使用fnend关键字定义一个函数。

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]

关键字列表要记住三个要点:

  1. 键必须是atoms
  2. 密钥是按开发人员指定的顺序排列的。
  3. 可以多次给出密钥。

对于数据库查询, 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

它们类似于关键字列表,但不完全相同-有两个区别:

  1. 映射允许将任何值用作键。
  2. 地图的键不遵循任何顺序。

注意: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}]

嵌套数据结构

数据通常需要层次结构和结构。 药剂提供支持mapsmaps ,或keyword listsmaps等。 使用put_inupdate_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(?<foo>d)/, "abcd")
%{"foo" => "d"}

iex> Regex.named_captures(~r/a(?<foo>b)c(?<bar>d)/, "abcd")
%{"bar" => "d", "foo" => "b"}

iex> Regex.named_captures(~r/a(?<foo>b)c(?<bar>d)/, "efgh")
nil

结论

Elixir是一个完全基于宏用法的功能齐全的元编程环境。 当它与元编程功能结合使用时,它强大的listsmapsanonymous functions可以帮助您将开发人员带入新的应用程序设计和数据结构水平。

我们可以看看我们的方法中的特定解决方案,由于采用DSL (特定领域语言)和Elixir的形式为Elixir提供了强大的财富,因此在非宏语言中可能需要创建许多行或代码类来创建。 Erlang的模块。

密钥对值存储操作, list和基本数据操作只是提供给开发人员的全部内容的一小部分。 我们将在本系列的下一部分继续介绍所有这些细节。

翻译自: https://code.tutsplus.com/tutorials/elixir-walkthrough-part-2-data-types--cms-27510

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值