คุณตรวจสอบประเภทของตัวแปรใน 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_*ฟังก์ชั่นเพื่อดำเนินการตามชนิดของตัวแปร

เรียนรู้คุณบ้าง 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 จริง ๆ แล้วไม่มีข้อมูลประเภทที่เก็บไว้? ฉันจำเป็นต้องสร้าง wrapper ใหม่ทั้งหมดบนประเภทที่มีอยู่เพื่อให้สามารถใช้ภาษาได้หรือไม่ Oo
Dmitry

2
@Dmitry คุณหมายถึงอะไรโดยการใช้งาน? ฉันสามารถดูตัวอย่างที่เป็นรูปธรรมที่คุณจะใช้ผลลัพธ์ของสิ่งที่ชอบได้typeof(variable)หรือไม่
whatyouhide

1
เมื่อโปรแกรมออกจากเวลารวบรวมและเข้าสู่รันไทม์ข้อมูลทั้งหมดเกี่ยวกับสิ่งที่วัตถุบางอย่างจะหายไป เมื่อฉันต้องการตรวจสอบข้อมูลของโปรแกรมที่กำลังทำงานวิธีเดียวที่จะรู้ว่าสิ่งที่เกิดขึ้นคือการตรวจสอบสิ่งต่าง ๆ ที่ถูกเปิดเผยผ่านเครือข่ายของแผนที่ หากข้อมูลประเภทไม่พร้อมใช้งานและฉันต้องการตรวจสอบประเภทนั้นมีค่าใช้จ่ายมากขึ้นในการวิเคราะห์วัตถุเพื่อให้ได้ 'ประเภทมากกว่าชนิดที่เปิดเผย typeof ช่วยให้เราสามารถวิเคราะห์ระบบที่กำลังทำงานและขยายออกไปที่รันไทม์ในลักษณะที่ช่วยให้การตรวจสอบประเภทและ polymorphism
Dmitry

2
จะเฉพาะเจาะจงมากขึ้น การใช้ typeof ที่มีประโยชน์ที่สุดคือความสามารถในการแมปตารางแฮชของ [type string, function] ไปยังรายการ unknowns โดยตรง ตัวอย่างเช่น; IO.puts ไม่สามารถแมปfoo = [1, "hello", [1, 2, 3]]ด้วยรหัสEnum.map(foo, fn(x) -> IO.puts x end)เพราะ [1,2, 3] จะถูกอ่านเป็นตัวอักษร (ทำไมต้อง erlang !!?) และจะแสดงใบหน้าที่ยิ้มแย้มให้คุณเห็น ดังนั้นเราจึงถูกบังคับให้ใช้การตรวจสอบแม้ว่าจำเป็นต้องทำการตรวจสอบหากเป็นรายการเท่านั้นมิฉะนั้นส่วนใหญ่เราไม่จำเป็นต้องใช้มัน typeof ให้เราเปลี่ยนว่า statement (O (n)) เป็นการค้นหาในพจนานุกรม (O (1))
Dmitry

1
@Dmitry สำหรับประเภทการใช้งาน Elixir โปรโตคอลจะเป็นประโยชน์ elixir-lang.org/getting-started/protocols.htmlคุณสามารถใช้Printableโพรโทคอลของคุณเองซึ่งล้อมและเปลี่ยนพฤติกรรมของการพิมพ์เช่นรายการจำนวนเต็ม ตรวจสอบให้แน่ใจว่าคุณไม่ได้ใช้กับรหัส Erlang หรือคุณจะเกาหัวสงสัยว่าทำไมแทนที่จะเป็นข้อความที่คุณเห็นรายการจำนวนเต็ม
Matt Jadczak

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- เหมือนกันสำหรับข้อมูล / 1
krivar

1
ยังคงใช้งานได้ใน Elixir 1.8.1 คุณต้องติดตั้งยาอายุวัฒนะรุ่นเก่ามาก
Fred the Magic Wonder Dog

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

อีกวิธีคือใช้การจับคู่รูปแบบ สมมติว่าคุณใช้ Timex ซึ่งใช้โครงสร้าง%DateTime{}และคุณต้องการดูว่าองค์ประกอบนั้นเป็นองค์ประกอบหรือไม่ คุณสามารถค้นหาการจับคู่โดยใช้การจับคู่รูปแบบในวิธีการ

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

def is_a_datetime?(_) do
  false
end

1
หรือตามคำตอบที่ยอมรับ แต่ไม่เน้น: »โดยปกติคุณต้องการทราบชนิดของตัวแปรเพื่อที่จะดำเนินการตาม« ในยาอายุวัฒนะคุณปฏิบัติตามโดยการจับคู่แบบไม่ได้โดย/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 ยังอยู่ในช่วงแรก แต่ก็สามารถทำได้และมากกว่านี้
Fred the Magic Wonder Dog

ผูกมัดตัวเองกับการพึ่งพาจากภายนอกได้อย่างง่ายดาย? จะปรับปรุงความสามารถในการแบ่งปันรหัสกับเพื่อน ๆ ได้อย่างไร นี่คือถนนถึง 2 ปัญหา
มิทรี

ขอบคุณสำหรับการแก้ไข @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 construct ดูคล้ายกับ qw // ฉันสงสัยว่า Perl มีกลไกที่ชาญฉลาดในการจำลองการอ้างอิงของ Lisplike หรือไม่
Dmitry

ฉันสงสัยว่าคำพูดทำงานอย่างไร สามารถจำลองได้โดยใช้ตัวประมวลผลพรีนนิพจน์ปกติหรือไม่หรือต้องการให้โปรแกรมแยกวิเคราะห์เดินดูโค้ดทั้งหมดเพื่อทำการขยายมาโคร
Dmitry

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.