如何检查Elixir中变量的类型


138

在Elixir中,如何检查类型(如Python):

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

我在Elixir中阅读了类型检查器,例如'is_bitstring','is_float','is_list','is_map'等,但是如果您不知道类型可能是什么呢?

Answers:


104

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

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

向您学习一些Erlang有一章很好地介绍了在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

3
Erlang / Elixir是否真的没有存储的类型信息?我是否真的需要在现有类型上创建一个全新的包装才能使该语言可用?Oo
Dmitry

2
@Dmitry可用表示什么?我可以看到一个具体的示例,在该示例中您将使用类似的结果typeof(variable)吗?
whatyouhide

1
当程序离开编译时间并进入运行时时,有关某个对象丢失的所有信息都将丢失。当我要检查正在运行的程序的信息时,唯一了解情况的方法是检查通过地图网络公开的内容。如果类型信息不可用,并且我要检查类型,则分析对象以获取其类型的成本要比已经公开的类型高得多。typeof允许我们分析正在运行的系统,并在运行时以允许类型检查和多态的方式对其进行扩展。
德米特里

2
更具体; typeof最有用的用途是能够将[type string,function]的哈希表直接映射到未知列表。例如; IO.puts不能foo = [1, "hello", [1, 2, 3]]用代码映射到,Enum.map(foo, fn(x) -> IO.puts x end)因为[1,2,3]将被读取为字符(为什么erlang !!?),并且会显示很多笑脸(尝试一下!)。因此,即使仅在清单时才需要检查,我们还是被迫使用检查,否则大多数时候我们就不需要它。typeof使我们可以将if语句(O(n))转换为字典查找(O(1))。
德米特里

1
@Dmitry对于这种使用类型的Elixir协议将很有用。elixir-lang.org/getting-started/protocols.html您可以实现自己的Printable协议,该协议可以包装和更改打印行为,例如整数列表。只要确保您不将其与Erlang代码一起使用-否则您将不知所措,想知道为什么看到的不是整数,而是消息的列表。
马特·贾德扎克

168

从elixir 1.2开始,iiex中有一个命令,该命令将列出类型以及更多任何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中的任何数据类型实现一个功能,执行此操作的方法是为要使用该功能的所有数据类型定义一个协议和该协议的实现。不幸的是,您不能在警卫中使用协议功能。但是,简单的“类型”协议将非常容易实现。


1
在2019年,这将返回一个错误undefined function i/1-与信息/ 1相同
krivar

1
在Elixir 1.8.1中仍然可以使用。您必须安装了非常旧的elixir版本。
弗雷德神奇魔术犬

2
@krivar @ fred-the-magic-wonder-dog你们都是正确的:)。 &i/1是的功能IEx.Helpers。如果将其&IEx.Helpers.i/1放入香草Elixir中,CompileError除非您已将其:iex作为应用程序包含在内,否则将生成一个mix.exs
popedotninja

39

同样出于调试目的,如果您不在iex中,则可以直接调用它:

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

1
如果要在日志中查看它,请添加IO.inspect(IEx.Info.info(5))
Guillaume

24

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

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

def is_a_datetime?(_) do
  false
end

1
或者,如已接受的答案所述,但并没有强调:»您通常想知道变量的类型以便采取相应措施«。在Elixir中,您通过模式匹配而不是switch/来相应地采取行动case
mariotomo

18

我将其留在这里,是为了希望有人找出一个理智的版本。目前,在Google上对此没有好的答案。

defmodule Util do
    def typeof(self) do
        cond do
            is_float(self)    -> "float"
            is_number(self)   -> "number"
            is_atom(self)     -> "atom"
            is_boolean(self)  -> "boolean"
            is_binary(self)   -> "binary"
            is_function(self) -> "function"
            is_list(self)     -> "list"
            is_tuple(self)    -> "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(self)
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

如果您确实需要“类型”检查器,则可以使用哲学家的石头组织中的工具轻松构建一个检查器。github.com/philosophers-stone。Phenetic仍处于早期阶段,但它可以做到的甚至更多。
弗雷德神奇狗

容易将自己绑定到外部依赖项?如何提高我与朋友共享代码的能力?这是通往两个问题的道路。
德米特里

感谢您的@aks编辑;我现在实际上可以回到4个空间了^ _ ^
德米特里(Dmitry)

15

我只是从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

巧妙使用报价!我看到的Elixir代码越多,就会使我想起Perl。〜w结构看起来与qw //非常相似。我想知道Perl是否具有某种聪明的机制来模拟Lisplike报价。
德米特里

我不知道报价是如何工作的。可以使用正则表达式预处理器对其进行仿真,还是需要解析器遍历整个代码来进行宏扩展。
德米特里

1

我碰到一种情况,需要检查参数是否需要确定类型。也许可以采用更好的方法。

像这样:

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

用法:

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

1

只是因为没有人提到它

IO.inspect/1

输出以控制台对象...几乎等同于JSON.stringify

当您无法终生确定对象在测试中的外观时,这将非常有帮助。


4
没有回答问题,甚至没有结束
LowFieldTheory
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.