你如何检查 Elixir 中的变量类型

How do you check for the type of variable in Elixir

在 Elixir 中,你如何检查 Python 中的类型:

>>> a = "test"
>>> type(a)
<type 'str'>
>>> b =10
>>> type(b)
<type 'int'>

我在 Elixir 中读到有类型检查器,例如 'is_bitstring'、'is_float'、'is_list'、'is_map' 等,但是如果您不知道类型是什么怎么办可能是?

在 Elixir/Erlang 中没有直接获取变量类型的方法。

您通常想知道变量的类型以便采取相应的行动;您可以使用 is_* 函数来根据变量的类型进行操作。

学习一些 Erlang a nice chapter 关于在 Erlang 中输入(以及在 Elixir 中)的内容。

使用 is_* 系列函数最惯用的方法可能是在模式匹配中使用它们:

def my_fun(arg) when is_map(arg), do: ...
def my_fun(arg) when is_list(arg), do: ...
def my_fun(arg) when is_integer(arg), do: ...
# ...and so on

从 elixir 1.2 开始,在 iex 中有一个 i 命令可以列出任何 Elixir 变量的类型和更多信息。

iex> foo = "a string" 
iex> i foo 
Term
 "a string"
Data type
 BitString
Byte size
 8
Description
 This is a string: a UTF-8 encoded binary. It's printed surrounded by
 "double quotes" because all UTF-8 encoded codepoints in it are        printable.
Raw representation
  <<97, 32, 115, 116, 114, 105, 110, 103>>
Reference modules
  String, :binary

如果您查看 i 命令的代码,您会发现这是通过协议实现的。

https://github.com/elixir-lang/elixir/blob/master/lib/iex/lib/iex/info.ex

如果你想在 Elixir 中为任何数据类型实现一个函数,方法是为你希望函数工作的所有数据类型定义一个协议和协议的实现。不幸的是,你不能在守卫中使用协议功能。然而,一个简单的 "type" 协议将非常容易实现。

另一种方法是使用模式匹配。假设您正在使用 Timex,它使用 %DateTime{} 结构,并且您想要查看某个元素是否为一个。您可以在方法中使用模式匹配找到匹配项。

def datetime?(%DateTime{}) do
  true
end

def datetime?(_) do
  false
end

为了有人希望找出一个真正合理的版本,我将把它留在这里。 google...

上出现的问题目前没有好的答案
defmodule Util do
    def typeof(a) do
        cond do
            is_float(a)    -> "float"
            is_number(a)   -> "number"
            is_atom(a)     -> "atom"
            is_boolean(a)  -> "boolean"
            is_binary(a)   -> "binary"
            is_function(a) -> "function"
            is_list(a)     -> "list"
            is_tuple(a)    -> "tuple"
            true           -> "idunno"
        end    
    end
end

为了完整起见,测试用例:

cases = [
    1.337, 
    1337, 
    :'1337', 
    true, 
    <<1, 3, 3, 7>>, 
    (fn(x) -> x end), 
    {1, 3, 3, 7}
]

Enum.each cases, fn(case) -> 
    IO.puts (inspect case) <> " is a " <> (Util.typeof case)
end

这是一个协议解决方案;我不确定它们是否更快(我当然希望它们不会对所有类型进行循环),但它非常丑陋(而且脆弱;如果他们添加或删除基本类型或重命名,它会破坏它)。

defprotocol Typeable, do: def typeof(a)
defimpl Typeable, for: Atom, do: def typeof(_), do: "Atom"
defimpl Typeable, for: BitString, do: def typeof(_), do: "BitString"
defimpl Typeable, for: Float, do: def typeof(_), do: "Float"
defimpl Typeable, for: Function, do: def typeof(_), do: "Function"
defimpl Typeable, for: Integer, do: def typeof(_), do: "Integer"
defimpl Typeable, for: List, do: def typeof(_), do: "List"
defimpl Typeable, for: Map, do: def typeof(_), do: "Map"
defimpl Typeable, for: PID, do: def typeof(_), do: "PID"
defimpl Typeable, for: Port, do: def typeof(_), do: "Port"
defimpl Typeable, for: Reference, do: def typeof(_), do: "Reference"
defimpl Typeable, for: Tuple, do: def typeof(_), do: "Tuple"

IO.puts Typeable.typeof "Hi"
IO.puts Typeable.typeof :ok

我只是粘贴了 https://elixirforum.com/t/just-created-a-typeof-module/2583/5 中的代码:)

defmodule Util do
  types = ~w[function nil integer binary bitstring list map float atom tuple pid port reference]
  for type <- types do
    def typeof(x) when unquote(:"is_#{type}")(x), do: unquote(type)
  end
end

同样为了调试,如果不在iex中,可以直接调用:

IEx.Info.info(5)
=> ["Data type": "Integer", "Reference modules": "Integer"]

我遇到过需要检查参数是否为特定类型的情况。也许可以激活更好的方法。

像这样:

@required [{"body", "binary"},{"fee", "integer"}, ...]
defp match_desire?({value, type}) do
  apply(Kernel, :"is_#{type}", [value])
end

用法:

Enum.map(@required, &(match_desire?/1))

只是因为没有人提到它

IO.inspect/1

控制对象的输出...它几乎等同于JSON.stringify

当您终生无法弄清楚某个对象在测试中的样子时,这非常有用。

Useful.typeof/1

受此话题的启发,我们将 typeof/1 添加到我们的 Useful 函数库中。
完全符合您的期望。

将其添加到 mix.exs 中的 deps

def deps do
  [
    {:useful, "~> 0.4.0"}
  ]
end

用法:

iex> pi = 3.14159
iex> Useful.typeof(pi)
"float"

iex> fun = fn (a, b) -> a + b end
iex> Useful.typeof(fun)
"function"

iex> Useful.typeof(&Useful.typeof/1)
"function"

iex> int = 42
iex> Useful.typeof(int)
"integer"

iex> list = [1,2,3,4]
iex> Useful.typeof(list)
"list"

文档https://hexdocs.pm/useful/Useful.html#typeof/1
一如既往,feedback/contributions/improvements非常欢迎。

基于实现 here,您可以针对 IEx.Info.info/1:

返回的元组进行模式匹配
defmodule Type do
  def of(x) do
    [{_, type} | _] = IEx.Info.info(x)

    type
  end
end

Type.of(1) # "Integer"

Python 使用 Elixir 协议键入

模块实现的近似值是@Dimitry 的回答

协议源代码

该代码对于答案来说很大,所以我创建了一个 github 要点。 Protocol implementation.

我是测试和 Elixir 的菜鸟,所以非常感谢任何改进。

文档