当前位置:  开发笔记 > 编程语言 > 正文

Elixir宏扩展问题,但只是在理解

如何解决《Elixir宏扩展问题,但只是在理解》经验,为你挑选了0个好方法。

作为我们Dev Book Club的一部分,我在Elixir中编写了一个随机密码生成器.决定使用元编程,然后用宏来写它来干一些东西.

这非常有效:

# 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

我想在Dict/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,我将能够解决这个问题,但如果有人有任何建议,我会很感激.

推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有