核心语言

我们首先来感受一下 Elm 代码!

我们的目标是熟悉函数,以便在稍后学习较大的示例时更自信地阅读 Elm 代码。

Elm 中最小的构建模块称为。其中包括诸如 42True"Hello!" 之类的值。

我们先从观察数字开始

[ { "input": "1 + 1", "value": "\u001b[95m2\u001b[0m", "type_": "number" } ]

此页面上的所有示例都是交互式的,因此单击此黑框 ⬆️,光标应当开始闪烁。输入 2 + 2,然后按 Enter 键。它应打印出 4。你应该能够以同样的方式与此页面上的所有示例进行交互!

尝试输入诸如 30 * 60 * 10002 ^ 4 等内容。它应该像计算器一样工作!

进行数学运算很好,但令人惊讶的是,在大多数程序中这并不常见!使用字符串进行操作更加常见,比如这样

[ { "input": "\"hello\"", "value": "\u001b[93m\"hello\"\u001b[0m", "type_": "String" }, { "input": "\"butter\" ++ \"fly\"", "value": "\u001b[93m\"butterfly\"\u001b[0m", "type_": "String" } ]

尝试使用 (++) 运算符 ⬆️,将一些字符串组合在一起

当我们开始编写函数对其进行转换时,这些基本值才会变得更有趣!

注意:了解更多有关 (+)(/)(++) 等运算符,请参阅 Basics 模块的文档。值得通读该软件包中的所有文档!

函数

函数是一种转换值的方法。输入一个值,生成另一个值。

例如,这里有一个 greet 函数,它输入一个名字并打招呼

[ { "add-decl": "greet", "input": "greet name =\n \"你好 \" ++ name ++ \"!\"\n", "value": "\u001b[36m<函数>\u001b[0m", "type_": "字符串 -> 字符串" }, { "input": "问候语 \"Alice\"", "value": "\u001b[93m\"你好 Alice!\"\u001b[0m", "type_": "字符串" }, { "input": "greet \"Bob\"", "value": "\u001b[93m\"Hello Bob!\"\u001b[0m", "type_": "字符串" } ]

尝试问候别人,如 "Stokely""Kwame" ⬆️

传递给函数的值通常称为参数,因此你可以说“greet 是一个带有单个参数的函数”。

好的,现在要解决问候语的问题了,那么一个带两个参数的 madlib 函数如何?

[ { "add-decl": "madlib", "input": "madlib animal adjective =\n \"高调的 \" ++ animal ++ \" 穿着 \" ++ adjective ++ \" 的短裤。\"\n", "value": "\u001b[36m<函数>\u001b[0m", "type_": "字符串 -> 字符串 -> 字符串" }, { "input": "madlib \"cat\" \"ergonomic\"", "value": "\u001b[93m\"高调的猫穿着符合人体工学的短裤。\"\u001b[0m", "type_": "字符串" }, { "input": "madlib (\"butter\" ++ \"fly\") \"metallic\"", "value": "\u001b[93m\"高调的蝴蝶穿着金属短裤。\"\u001b[0m", "type_": "字符串" } ]

尝试给 madlib 函数添加两个参数 ⬆️

注意在第二个示例中,我们如何使用括号将 "butter" ++ "fly" 组合在一起。每个参数需要是原始值,例如 "cat",或者需要用括号括起来!

注意:来自 JavaScript 等语言的人可能会惊讶,因为这里的函数看起来不同

madlib "cat" "ergonomic"                  -- Elm
madlib("cat", "ergonomic")                // JavaScript

madlib ("butter" ++ "fly") "metallic"      -- Elm
madlib("butter" + "fly", "metallic")       // JavaScript

这乍一看可能令人惊讶,但这种风格最终使用了更少的括号和逗号。一旦你习惯了它,它会让语言感觉非常干净和简约!

If 表达式

当你想在 Elm 中有条件行为时,可以使用 if 表达式。

让我们创建一个新的 greet 函数,以便对亚伯拉罕·林肯总统表示适当的尊重

[ { "add-decl": "greet", "input": "greet name =\n if name == \"Abraham Lincoln\" then\n \"Greetings Mr. President!\"\n else\n \"Hey!\"\n", "value": "\u001b[36m<函数>\u001b[0m", "type_": "字符串 -> 字符串" }, { "input": "greet \"Tom\"", "value": "\u001b[93m\"Hey!\"\u001b[0m", "type_": "字符串" }, { "input": "greet \"Abraham Lincoln\"", "value": "\u001b[93m\"Greetings Mr. President!\"\u001b[0m", "type_": "字符串" } ]

可能还有其他需要涵盖的情况,但暂时这样做就足够了!

列表

列表是 Elm 中最常见的数据结构之一。它们包含一系列相关的事物,类似于 JavaScript 中的数组。

列表可以容纳多个值。这些值都必须具有相同的类型。以下是一些使用来自 List 模块的函数的示例

[ { "add-decl": "names", "input": "names =\n [ \"Alice\", \"Bob\", \"Chuck\" ]\n", "value": "[\u001b[93m\"Alice\"\u001b[0m,\u001b[93m\"Bob\"\u001b[0m,\u001b[93m\"Chuck\"\u001b[0m]", "type_": "列表 字符串" }, { "input": "List.isEmpty names", "value": "\u001b[96m否\u001b[0m", "type_": "布尔值" }, { "input": "List.length names", "value": "\u001b[95m3\u001b[0m", "type_": "字符串" }, { "input": "List.reverse names", "value": "[\u001b[93m\"Chuck\"\u001b[0m,\u001b[93m\"Bob\"\u001b[0m,\u001b[93m\"Alice\"\u001b[0m]", "type_": "列表 字符串" }, { "add-decl": "numbers", "input": "numbers =\n [4,3,2,1]\n", "value": "[\u001b[95m4\u001b[0m,\u001b[95m3\u001b[0m,\u001b[95m2\u001b[0m,\u001b[95m1\u001b[0m]", "type_": "列表 数字" }, { "input": "List.sort numbers", "value": "[\u001b[95m1\u001b[0m,\u001b[95m2\u001b[0m,\u001b[95m3\u001b[0m,\u001b[95m4\u001b[0m]", "type_": "列表 数字" }, { "add-decl": "increment", "input": "increment n =\n n + 1\n", "value": "\u001b[36m<函数>\u001b[0m", "type_": "数字 -> 数字" }, { "input": "List.map increment numbers", "value": "[\u001b[95m5\u001b[0m,\u001b[95m4\u001b[0m,\u001b[95m3\u001b[0m,\u001b[95m2\u001b[0m]", "type_": "列表 数字" } ]

试着创建自己的列表并使用函数如 List.length ⬆️

请记住,列表中的所有元素必须为同一类型!

元组

元组是另一种有用的数据结构。元组可以包含两个或三个值,且每个值可以为任意类型。常见用途是使用函数返回多个值。以下函数获取一个名称并将消息提供给用户

[ { "add-decl": "isGoodName", "input": "isGoodName name =\n if String.length name <= 20 then\n (True, \"name accepted!\")\n else\n (False, \"name was too long; please limit it to 20 characters\")\n", "value": "\u001b[36m<函数>\u001b[0m", "type_": "字符串 -> ( 布尔值, 字符串 )" }, { "input": "isGoodName \"Tom\"", "value": "(\u001b[96mTrue\u001b[0m, \u001b[93m\"name accepted!\"\u001b[0m)", "type_": "( 布尔值, 字符串 )" } ]

这会非常方便,但当事情变得复杂时,通常最好使用记录代替元组。

记录

记录可以包含许多值,且每个值均与一个名称关联。

以下是代表英国经济学家 John A. Hobson 的记录

[ { "add-decl": "john", "input": "john =\n { first = \"John\"\n , last = \"Hobson\"\n , age = 81\n }\n", "value": "{ \u001b[37mage\u001b[0m = \u001b[95m81\u001b[0m, \u001b[37mfirst\u001b[0m = \u001b[93m\"John\"\u001b[0m, \u001b[37mlast\u001b[0m = \u001b[93m\"Hobson\"\u001b[0m }", "type_": "{ age : 数字,first : 字符串,last : 字符串 }" }, { "input": "john.last", "value": "\u001b[93m\"Hobson\"\u001b[0m", "type_": "字符串" } ]

我们定义了一个包含三个的记录,其中包含有关 John 的姓名和年龄的信息。

试着访问其他域,如 john.age ⬆️

您还可以通过以下方式使用“域访问函数”来访问记录域

[ { "add-decl": "john", "input": "john = { first = \"John\", last = \"Hobson\", age = 81 }", "value": "{ \u001b[37mage\u001b[0m = \u001b[95m81\u001b[0m, \u001b[37mfirst\u001b[0m = \u001b[93m\"John\"\u001b[0m, \u001b[37mlast\u001b[0m = \u001b[93m\"Hobson\"\u001b[0m }", "type_": "{ age : number, first : String, last : String }" }, { "input": ".last john", "value": "\u001b[93m\"Hobson\"\u001b[0m", "type_": "String" }, { "input": "List.map .last [john,john,john]", "value": "[\u001b[93m\"Hobson\"\u001b[0m,\u001b[93m\"Hobson\"\u001b[0m,\u001b[93m\"Hobson\"\u001b[0m]", "type_": "List String" } ]

更新记录中的值通常是有用的

[ { "add-decl": "john", "input": "john = { first = \"John\", last = \"Hobson\", age = 81 }", "value": "{ \u001b[37mage\u001b[0m = \u001b[95m81\u001b[0m, \u001b[37mfirst\u001b[0m = \u001b[93m\"John\"\u001b[0m, \u001b[37mlast\u001b[0m = \u001b[93m\"Hobson\"\u001b[0m }", "type_": "{ age : number, first : String, last : String }" }, { "input": "{ john | last = \"Adams\" }", "value": "{ \u001b[37mage\u001b[0m = \u001b[95m81\u001b[0m, \u001b[37mfirst\u001b[0m = \u001b[93m\"John\"\u001b[0m, \u001b[37mlast\u001b[0m = \u001b[93m\"Adams\"\u001b[0m }", "type_": "{ age : number, first : String, last : String }" }, { "input": "{ john | age = 22 }", "value": "{ \u001b[37mage\u001b[0m = \u001b[95m22\u001b[0m, \u001b[37mfirst\u001b[0m = \u001b[93m\"John\"\u001b[0m, \u001b[37mlast\u001b[0m = \u001b[93m\"Hobson\"\u001b[0m }", "type_": "{ age : number, first : String, last : String }" } ]

如果你想大声说出这些表达式,你可以说类似于“我想要一个新的 John,他的姓是 Adams”或“一个年龄为 22 岁的人”。

请注意,当我们更新 `john` 的一些字段时,我们会创建一个全新的记录。它不会覆盖现有的记录。Elm 通过尽可能多地共享内容来实现这种高效。如果你更新十个字段中的一个,则新记录会共享九个未更改的值。

因此,一个更新年龄的函数可能如下所示

[ { "add-decl": "celebrateBirthday", "input": "celebrateBirthday person =\n { person | age = person.age + 1 }\n", "value": "\u001b[36m<function>\u001b[0m", "type_": "{ a | age : number } -> { a | age : number }" }, { "add-decl": "john", "input": "john = { first = \"John\", last = \"Hobson\", age = 81 }", "value": "{ \u001b[37mage\u001b[0m = \u001b[95m81\u001b[0m, \u001b[37mfirst\u001b[0m = \u001b[93m\"John\"\u001b[0m, \u001b[37mlast\u001b[0m = \u001b[93m\"Hobson\"\u001b[0m }", "type_": "{ age : number, first : String, last : String }" }, { "input": "celebrateBirthday john", "value": "{ \u001b[37mage\u001b[0m = \u001b[95m82\u001b[0m, \u001b[37mfirst\u001b[0m = \u001b[93m\"John\"\u001b[0m, \u001b[37mlast\u001b[0m = \u001b[93m\"Hobson\"\u001b[0m }", "type_": "{ age : number, first : String, last : String }" } ]

这样更新记录字段真的很常见,因此我们在下一部分中会看到更多内容!

个结果与 "" 匹配

    没有结果与 "" 匹配