Як перевірити тип змінної в 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 Some 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 немає збереженої інформації про тип? Чи дійсно мені потрібно створити цілу нову обгортку над існуючими типами, щоб мова була зручною для використання? Оо
Дмитро

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 дозволяє нам перетворити, якщо заяви (O (n)) в пошук словника (O (1)).
Дмитро

1
@Dmitry для такого типу використання протоколів Elixir буде корисним. elixir-lang.org/getting-started/protocols.html Ви можете реалізувати власний Printableпротокол, який завершує та змінює поведінку друку, наприклад, списки цілих чисел. Просто переконайтеся, що ви не використовуєте його з кодом Ерланга, або ви будете дряпати голову, цікаво, чому замість повідомлень ви бачите списки цілих чисел.
Мет Ядзак

168

Починаючи з elixir 1.2, iв iex є команда, яка перелічить тип і більше будь-якої змінної 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- те саме для info / 1
krivar

1
Це все ще працює в Elixir 1.8.1. У вас повинна бути встановлена ​​дуже стара версія еліксиру.
Fred Fred Magic Wonder Dog

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))
Гійом

24

Іншим підходом є використання відповідності шаблонів. Скажіть, ви використовуєте Timex, який використовує %DateTime{}структуру, і ви хочете побачити, чи є елемент. Ви можете знайти збіг, використовуючи відповідність шаблонів у методі.

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 . Фенетичний ще в перші дні, але він може це зробити і багато іншого.
Fred Fred Magic Wonder Dog

легко прив’язати себе до зовнішньої залежності? як це покращить мою здатність ділитися кодом з друзями? Це дорога до 2 проблем.
Дмитро

Дякую за редагування @aks; Я фактично можу повернутися до 4-х просторів зараз ^ _ ^
Дмитро

15

Я просто вставляю код з https://elixirforum.com/t/just-create-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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.