Làm thế nào để bạn kiểm tra loại biến trong Elixir


138

Trong Elixir, làm thế nào để bạn kiểm tra loại như trong Python:

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

Tôi đọc trong Elixir có các trình kiểm tra loại như 'is_bitopes', 'is_float', 'is_list', 'is_map', v.v., nhưng nếu bạn không biết loại này có thể là gì thì sao?

Câu trả lời:


104

Không có cách trực tiếp nào để có được loại biến trong Elixir / Erlang.

Bạn thường muốn biết loại biến để hành động tương ứng; bạn có thể sử dụng các is_*hàm để hành động dựa trên loại biến.

Tìm hiểu về bạn Một số Erlang có một chương hay về cách gõ Erlang (và do đó trong Elixir).

Cách thành ngữ nhất để sử dụng is_*họ các hàm có thể là sử dụng chúng trong các khớp mẫu:

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 thực sự không có thông tin loại được lưu trữ? Tôi có thực sự cần phải tạo một trình bao bọc hoàn toàn mới trên các loại hiện có để ngôn ngữ có thể sử dụng được không? Oo
Dmitry

2
@Dmitry bạn có ý nghĩa gì khi sử dụng? Tôi có thể xem một ví dụ cụ thể nơi bạn sẽ sử dụng kết quả của một cái gì đó như thế typeof(variable)nào không?
whatyou leather

1
Khi một chương trình rời khỏi thời gian biên dịch và vào thời gian chạy, tất cả thông tin về những gì một đối tượng bị mất. Khi tôi muốn kiểm tra thông tin của một chương trình đang chạy, cách duy nhất để biết chuyện gì đang xảy ra là kiểm tra những thứ được phơi bày qua mạng lưới bản đồ. nếu thông tin loại không có sẵn và tôi muốn kiểm tra loại, sẽ tốn nhiều chi phí hơn để phân tích đối tượng để lấy loại của nó so với khi loại đã được phơi bày. typeof cho phép chúng ta phân tích hệ thống đang chạy và mở rộng nó trong thời gian chạy theo cách cho phép đánh máy và đa hình.
Dmitry

2
Để cụ thể hơn; việc sử dụng typeof hữu ích nhất là khả năng ánh xạ trực tiếp một bảng băm của [kiểu chuỗi, hàm] vào danh sách các ẩn số. Ví dụ; IO.puts không thể được ánh xạ foo = [1, "hello", [1, 2, 3]], với mã Enum.map(foo, fn(x) -> IO.puts x end)bởi vì [1,2, 3] sẽ được đọc dưới dạng ký tự (tại sao erlang !!?), Và sẽ hiển thị cho bạn một loạt các khuôn mặt nhếch nhác (hãy thử!). vì vậy chúng tôi buộc phải sử dụng kiểm tra mặc dù việc kiểm tra chỉ cần thiết nếu đó là một danh sách, nếu không thì hầu hết thời gian chúng tôi không cần đến nó. typeof cho phép chúng ta biến if statement (O (n)) thành tra cứu từ điển (O (1)).
Dmitry

1
@Dmitry cho loại sử dụng giao thức Elixir sẽ hữu ích. elixir-lang.org/getting-started/prot Protocol.html Bạn có thể thực hiện Printablegiao thức của riêng mình , nó bao bọc và thay đổi hành vi in, ví dụ như danh sách các số nguyên. Chỉ cần đảm bảo rằng bạn không sử dụng nó với mã Erlang, hoặc bạn sẽ tự gãi đầu tự hỏi tại sao thay vì tin nhắn bạn đang xem danh sách các số nguyên.
Matt Jadczak

168

Bắt đầu từ elixir 1.2, có một ilệnh trong iex sẽ liệt kê loại và nhiều hơn bất kỳ biến Elixir nào.

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

Nếu bạn nhìn vào mã cho ilệnh, bạn sẽ thấy rằng điều này được thực hiện thông qua Giao thức.

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

Nếu bạn muốn triển khai một chức năng cho bất kỳ loại Dữ liệu nào trong Elixir, cách thực hiện là xác định Giao thức và triển khai Giao thức cho tất cả các loại dữ liệu bạn muốn chức năng hoạt động. Thật không may, bạn không thể sử dụng chức năng Giao thức trong bộ bảo vệ. Tuy nhiên, một giao thức "loại" đơn giản sẽ rất đơn giản để thực hiện.


1
vào năm 2019, điều này trả về một lỗi undefined function i/1- tương tự cho thông tin / 1
krivar

1
Điều này vẫn hoạt động trong Elixir 1.8.1. Bạn phải cài đặt phiên bản thuốc tiên rất cũ.
Fred the Magic Wonder Dog

2
@krivar @ fred-the-Magic-wonder-dog bạn đều đúng :). &i/1là một chức năng trên IEx.Helpers. Nếu bạn đặt &IEx.Helpers.i/1vào vanilla Elixir của mình, bạn sẽ tạo ra CompileErrortrừ khi bạn đưa vào :iexlàm ứng dụng trong mix.exs.
popedotninja

39

Ngoài ra, với mục đích gỡ lỗi, nếu bạn không ở iex, bạn có thể gọi trực tiếp:

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

1
Nếu bạn muốn thấy nó trong nhật ký của mình, hãy thêm IO.inspect (IEx.Info.info (5))
Guillaume

24

Một cách tiếp cận khác là sử dụng khớp mẫu. Giả sử bạn đang sử dụng Timex, sử dụng một %DateTime{}cấu trúc và bạn muốn xem liệu một phần tử có phải là một thành phần không. Bạn có thể tìm thấy kết quả khớp bằng cách sử dụng khớp mẫu trong phương thức.

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

def is_a_datetime?(_) do
  false
end

1
hoặc, như câu trả lời được chấp nhận đã nhận xét nhưng không nhấn mạnh: »Bạn thường muốn biết loại biến để hành động tương ứng«. trong Elixir, bạn hành động tương ứng bằng cách khớp mẫu, không phải bởi switch/ case.
mariotomo

18

Tôi sẽ để nó ở đây vì lợi ích của ai đó hy vọng tìm ra một phiên bản thực sự lành mạnh. Hiện tại không có câu trả lời hay nào cho vấn đề này sắp xuất hiện trên 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

Để hoàn thiện, các trường hợp thử nghiệm:

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

Đây là một giải pháp với các giao thức; Tôi không chắc liệu chúng có nhanh hơn không (tôi chắc chắn hy vọng chúng không thực hiện một vòng lặp trên tất cả các loại), nhưng nó khá xấu (và dễ vỡ; nếu chúng thêm hoặc xóa một loại cơ bản hoặc đổi tên, nó sẽ phá vỡ nó).

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

Nếu bạn thực sự muốn một trình kiểm tra "loại", bạn có thể dễ dàng xây dựng một trình kiểm tra bằng cách sử dụng các công cụ trong org org của nhà triết học. github.com/philosophers-stone . Phenetic vẫn còn trong những ngày đầu, nhưng nó có thể làm điều này và nhiều hơn nữa.
Fred the Magic Wonder Dog

dễ dàng ràng buộc bản thân với một phụ thuộc bên ngoài? Làm thế nào để cải thiện khả năng chia sẻ mã của tôi với bạn bè? Đây là một con đường đến 2 vấn đề.
Dmitry

Cảm ơn đã chỉnh sửa @aks; Tôi thực sự có thể quay lại 4 chỗ ngay bây giờ ^ _ ^
Dmitry

15

Tôi chỉ cần dán mã từ 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

Khéo léo sử dụng báo giá! Tôi càng thấy mã Elixir, nó càng khiến tôi nhớ đến Perl; that ~ w xây dựng trông rất giống với qw //. Tôi tự hỏi nếu Perl có một số cơ chế thông minh để mô phỏng trích dẫn Lisplike.
Dmitry

Tôi tự hỏi làm thế nào trích dẫn hoạt động; nó có thể được mô phỏng bằng bộ xử lý biểu thức chính quy hay nó yêu cầu trình phân tích cú pháp đi qua toàn bộ mã để thực hiện mở rộng macro.
Dmitry

1

Tôi đã gặp một tình huống cần kiểm tra tham số cần phải là loại nhất định. Có lẽ có thể hoạt động một cách tốt hơn.

Như thế này:

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

Sử dụng:

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

1

Chỉ vì không ai nhắc đến nó

IO.inspect/1

Đầu ra để điều khiển đối tượng ... nó gần như tương đương với JSON.opesify

Rất hữu ích khi bạn không thể tìm ra cuộc sống của một đối tượng trong một bài kiểm tra.


4
Không phải là một câu trả lời cho câu hỏi, thậm chí không gần gũi
LowFieldTheory
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.