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」などのタイプチェッカーがありますが、タイプが何であるかわからない場合はどうなりますか?

回答:


104

Elixir / Erlangで変数の型を直接取得する方法はありません。

通常は、それに応じて動作するために変数のタイプを知りたいです。is_*変数のタイプに基づいて機能するために関数を使用できます。

Learn You一部の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 2016年

1
プログラムがコンパイル時間を離れてランタイムに入ると、オブジェクトの内容に関するすべての情報が失われます。実行中のプログラムの情報を検査したい場合、何が起こっているのかを知る唯一の方法は、マップのネットワークを介して公開されているものを検査することです。型情報が利用できず、型を調べたい場合、型が既に公開されている場合よりも、オブジェクトを分析して型を取得する方がはるかにコストがかかります。typeofを使用すると、実行中のシステムを分析し、型チェックとポリモーフィズムを可能にする方法で実行時にシステムを拡張できます。
Dmitry

2
もう少し詳しく言うと; typeofの最も有用な使用法は、[type string、function]のハッシュテーブルを未知数のリストに直接マップする機能です。例えば; IO.putsは、[1,2、3]が文字として読み取られるため(なぜerlang !!?)、たくさんのにこやかな顔が表示されるため(foo = [1, "hello", [1, 2, 3]]コードをEnum.map(foo, fn(x) -> IO.puts x end)試してみてください)、コードを使用してマッピングできません。そのため、inspectはリストの場合にのみ必要ですが、それ以外の場合はほとんど必要ないのに、inspectを使用する必要があります。typeofを使用すると、ifステートメント(O(n))を辞書検索(O(1))に変換できます。
Dmitry

1
そのタイプの使用には@Dmitry Elixirプロトコルが役立ちます。elixir-lang.org/getting-started/protocols.htmlPrintable整数のリストなど、印刷の動作をラップして変更する独自のプロトコルを実装できます。Erlangコードでは使用しないでください。そうしないと、メッセージの代わりに整数のリストが表示されるのではないかと不思議に思います。
Matt Jadczak 2016

168

elixir 1.2以降、iすべてのElixir変数のタイプなどをリストするコマンドがiexにあります。

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
-info

1
これはElixir 1.8.1でも機能します。非常に古いバージョンのエリクサーがインストールされている必要があります。
フレッド・ザ・マジック・ワンダードッグ、

2
@krivar @ fred-the-magic-wonder-dogあなたはどちらも正しいです:) &i/1はの関数IEx.Helpersです。&IEx.Helpers.i/1バニラエリクサーに入れると、アプリケーションに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を使用していて、要素が1つであるかどうかを確認するとします。メソッドでパターンマッチングを使用して一致を見つけることができます。

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

def is_a_datetime?(_) do
  false
end

1
または、受け入れられた答えが強調したが強調しなかったように:»通常、それに応じて動作するために変数のタイプを知りたい«。エリクサーでは、switch/ ではなくパターンマッチングによって適切に動作しますcase
mariotomo

18

誰かが実際に健全なバージョンを理解できるように、これをここに残しておきます。現時点では、これがグーグルで出てくるのに良い答えはありません...

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はまだ初期の段階ですが、これ以外にも多くのことが可能です。
フレッドザマジックワンダードッグ

簡単に自分を外部の依存関係にバインドしますか?友達とコードを共有する私の能力をどのように向上させるでしょうか?これは2つの問題への道です。
Dmitry

編集@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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.