提问人:miccx 提问时间:11/11/2023 更新时间:11/14/2023 访问量:102
这两种理解有何不同?
How are these two comprehensions different?
问:
我是 Elixir 的新手(一般也是编程),我不明白 Elixir 在这里启用字符串和 ASCII 之间的解释是什么。
result = for << n <- "abc132" >>, do: n + 1
IO.puts(result) #bcd243 - I assume this converts to ASCII and then back to string
string = "abc123"
for n <- String.to_charlist(string) do
IO.puts(n+1) #98,99,100,50,51,52 - this converts to ASCII, but doesn't convert back to string?
end
答:
Elixir 有两种不同的字符串表示形式(Erlang 也是如此)。默认的 Elixir String 是 UTF-8 编码的 Erlang 对象;“Charlist”的字面意思是字符列表。binary
这里的一个重要技术说明是,该字符串是 UTF-8 编码的 Unicode,不一定是 ASCII。如果您的字符串仅包含 ASCII 字符,则它是 ASCII,但您通常不能保证这一点。二进制文件很可能包含与单个字符对应的多个字节。例如,您可以手写出 U+00A0 NO-BREAK SPACE
iex> <<0xc2, 0xa0>>
" "
iex> <<0xc2, 0xa0>> |> String.length()
1
这是两个字节,但一个字符。
在第二个循环中,您已将字符串转换为代码点列表,然后操作代码点。由于备用字符串表示形式是代码点列表,因此可以将每个代码点包装在一个列表中
for n <- String.to_charlist(string) do
IO.puts([n+1]) # creating a single-character charlist-format string
end
但是,由于 charlist 是一个列表,您也可以使用正常的功能技术来操作它。
iex> string |>
...> String.to_charlist() |>
...> Enum.map(fn c -> c + 1 end) |>
...> to_string()
"bcd234"
最后一种形式并不特别依赖于字符串是 ASCII。
IO.puts(result) #bcd243 - 我假设这转换为 ASCII,然后 返回字符串
您的两个示例等效于以下内容:
iex(14)> IO.puts [97, 98, 99]
abc
:ok
iex(15)> IO.puts 97
97
:ok
iex(16)> IO.puts 98
98
:ok
iex(17)> IO.puts 99
99
:ok
for()
返回一个列表:
iex(5)> result = for << n <- "abc132" >>, do: n
'abc132' <<<====******
请注意单引号。单引号表示字符列表,其中字符列表是整数列表。事实上,像这样的字符列表只是写作的捷径:'abc'
[97,98,99]
iex(13)> 'abc' === [97,98,99]
true
花点时间让它沉入其中。你必须意识到,写作和在代码中没有区别。'abc'
[97,98,99]
许多混淆的根源是,对字符列表和字符串显示相同的内容:IO.puts()
iex(6)> IO.puts 'abc132'
abc132
:ok
iex(23)> IO.puts [97,98,99,49,50,51]
abc123
:ok
iex(7)> IO.puts "abc132"
abc132
:ok
当你在做一些数学运算时,这真的是没有帮助的,例如:假设你在一个列表中收集了一些学生的考试成绩,你想在计算他们的平均值之前显示考试成绩,而考试成绩恰好是,对于输出,你看到的而不是。你可能会扯掉一些头发,试图弄清楚到底发生了什么。[97,98,99]
abc
[97,98,99]
查看输出可能会非常令人困惑。要确定某物到底是什么,请使用而不是:IO.puts
IO.inspect
IO.puts
iex(1)> result = 'abc'
'abc'
iex(2)> IO.puts result
abc
:ok
iex(3)> IO.inspect result
'abc'
'abc'
您可以更进一步,将 iex 配置为显示字符列表的真实情况,即整数列表:
iex(4)> IEx.configure(inspect: [charlists: :as_lists])
:ok
iex(5)> IO.inspect result
'abc'
[97, 98, 99]
您甚至可以创建一个文件,以便在每次 iex 启动时自动设置该配置。.iex.exs
接下来,当您在 :for()
<< n <- "abc132" >>
每个都是一个整数,表示双引号字符串中的一个字节。字符串中的字符都有一个字节的整数代码,但对于由多个字节整数代码表示的字符,例如“带波浪号的拉丁大写字母 A”: Ã 将仅是 UTF-8 整数代码的一个字节:n
n
iex(5)> str = "\xC3\x83"
"Ã"
iex(6)> for << n <- str >>, do: IO.puts n
195
131
[:ok, :ok]
在第二个示例中:
string = "abc123"
for n <- String.to_charlist(string) do
IO.puts(n+1) #98,99,100,50,51,52 - this converts to ASCII, but doesn't convert back to string?
end
将字符串转换为 charlist:
String.to_charlist(string)
如前所述,charlist 是一个整数列表。因此,您的生成器将变为:
n <- [97,98,99,49,50,51]
其中 each 是列表中的整数之一。然后,在整数加 1 上使用,该整数显示为整数:n
IO.puts
iex(3)> n = 97
97
iex(4)> IO.puts n
97
:ok
iex(5)> IO.puts n+1
98
:ok
当您给出单个整数时,它会输出整数。IO.puts
在第二个示例中,您从不使用 返回的列表,但是由于 返回 了每个 ,因此返回的列表仅由 的 组成。for()
IO.puts()
:ok
n
:ok
总而言之,在两个示例中,生成器都返回相同的整数,但在第一个示例中,您在整个结果列表中使用,该列表显示为字符串;在第二个示例中,您对单个整数使用。IO.puts
IO.puts
评论