gpt4 book ai didi

macros - Elixir 宏扩展问题,但仅在一个理解中

转载 作者:行者123 更新时间:2023-12-04 15:20:22 25 4
gpt4 key购买 nike

作为我们 Dev Book Club 工作的一部分,我在 Elixir 中编写了一个随 secret 码生成器。决定玩元编程,并用宏编写它来干点事情。

这完美地工作:

# lib/macros.ex
defmodule Macros do
defmacro define_alphabet(name, chars) do
len = String.length(chars) - 1

quote do
def unquote(:"choose_#{name}")(chosen, 0) do
chosen
end

def unquote(:"choose_#{name}")(chosen, n) do
alphabet = unquote(chars)

unquote(:"choose_#{name}")([(alphabet |> String.at :random.uniform(unquote(len))) | chosen], n - 1)
end
end
end
end

# lib/generate_password.ex
defmodule GeneratePassword do
require Macros

Macros.define_alphabet :alpha, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Macros.define_alphabet :special, "~`!@#$%^&*?"
Macros.define_alphabet :digits, "0123456789"

def generate_password(min_length, n_special, n_digits) do
[]
|> choose_alpha(min_length - n_special - n_digits)
|> choose_special(n_special)
|> choose_digits(n_digits)
|> Enum.shuffle
|> Enum.join
end
end

我想在字典/ map 甚至列表中定义字母,并对其进行迭代以调用 Macros.define_alphabet,而不是手动调用 3 次。但是,当我尝试使用下面的代码时,无论我使用什么结构来保存字母,它都会导致编译失败。
alphabets = %{
alpha: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
special: "~`!@#$%^&*?",
digits: "0123456789",
}

for {name, chars} <- alphabets, do: Macros.define_alphabet(name, chars)

给出以下错误:
Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Compiled lib/macros.ex

== Compilation error on file lib/generate_password.ex ==
** (FunctionClauseError) no function clause matching in String.Graphemes.next_grapheme_size/1
(elixir) unicode/unicode.ex:231: String.Graphemes.next_grapheme_size({:chars, [line: 24], nil})
(elixir) unicode/unicode.ex:382: String.Graphemes.length/1
expanding macro: Macros.define_alphabet/2
lib/generate_password.ex:24: GeneratePassword (module)
(elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

我尝试过将字母表映射为列表列表、元组列表、原子映射->字符串和字符串->字符串,这似乎并不重要。我还尝试将这些对通过管道传输到 Enum.each 中,而不是使用“for”理解,如下所示:
alphabets |> Enum.each fn {name, chars} -> Macros.define_alphabet(name, chars) end

所有这些都给出相同的结果。认为这可能与调用 :random.uniform 有关,并将其更改为:
alphabet |> to_char_list |> Enum.shuffle |> Enum.take(1) |> to_string

这只是将错误稍微改变为:
Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]


== Compilation error on file lib/generate_password.ex ==
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:name, [line: 24], nil}
(elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir) lib/string/chars.ex:17: String.Chars.to_string/1
expanding macro: Macros.define_alphabet/2
lib/generate_password.ex:24: GeneratePassword (module)
(elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

即使进行了这种更改,当我像顶部一样手动调用 Macros.define_alphabet 时也能正常工作,但当我以任何理解或使用 Enum.each 时就不行。

这不是什么大问题,但我希望能够根据用户定义的配置以编程方式在字母列表中添加和删除。

我敢肯定,随着我深入了解 Metaprogramming Elixir ,我将能够弄清楚这一点,但是如果有人有任何建议,我将不胜感激。

最佳答案

列表推导是一种使用一个列表并从中获取另一个列表(或一般情况下为 Enumerable)的方法。在您的情况下,您不想获得新列表,而是想在模块中定义函数。因此,列表推导不是合适的方法。

您可以使用另一个宏来定义 map 中的字母。

关于macros - Elixir 宏扩展问题,但仅在一个理解中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34300162/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com