İksir'de değişken türünü nasıl kontrol edersiniz?


138

İksir'de Python gibi türü nasıl kontrol edersiniz:

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

Elixir'de 'is_bitstring', 'is_float', 'is_list', 'is_map' vb.Gibi tür denetleyicileri okudum, ancak türün ne olabileceği hakkında hiçbir fikriniz yoksa ne olur?

Yanıtlar:


104

Elixir / Erlang'da bir değişkenin türünü almanın doğrudan bir yolu yoktur.

Buna göre hareket etmek için genellikle bir değişkenin türünü bilmek istersiniz; is_*bir değişkenin türüne göre hareket etmek için işlevleri kullanabilirsiniz .

Sizi Öğrenin Bazı Erlang'ın Erlang'da (ve böylece İksir'de) yazmayla ilgili güzel bir bölümü vardır .

is_*İşlev ailesini kullanmanın en deyimsel yolu muhtemelen bunları kalıp eşleşmelerinde kullanmak olacaktır:

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 gerçekten depolanmış tip bilgisine sahip değil mi? Dilin kullanılabilir olması için mevcut türler üzerinde yepyeni bir paket oluşturmam gerekiyor mu? Oo
Dmitry

2
@Dmitry ne demek? Böyle bir şeyin sonucunu kullanabileceğiniz somut bir örnek görebilir miyim typeof(variable)?
whatyouhide

1
Bir program derleme zamanından çıkıp çalışma zamanına girdiğinde, bazı nesnelerin kaybolmasına ilişkin tüm bilgiler kaybolur. Çalışan bir programın bilgilerini incelemek istediğimde, neler olup bittiğini bilmenin tek yolu bir harita ağı aracılığıyla ortaya çıkan şeyleri incelemek. tür bilgisi yoksa ve türü incelemek istiyorum, nesneyi türünü elde etmek için, türü zaten açıklanmış olandan çok daha pahalıya mal olur. typeof, çalışan sistemi analiz etmemize ve çalışma zamanında daktilo kontrolüne ve polimorfizme izin verecek şekilde genişletmemize olanak tanır.
Dmitry

2
Daha spesifik olmak gerekirse; typeof'in en kullanışlı kullanımı, [type string, function] öğesinin bir karma tablosunu bilinmeyenler listesine doğrudan eşleme yeteneğidir. Örneğin; IO.puts foo = [1, "hello", [1, 2, 3]]kodu ile eşlenemez , Enum.map(foo, fn(x) -> IO.puts x end)çünkü [1,2, 3] karakter olarak okunacaktır (neden erlang !!?) Ve size bir sürü gülen yüz gösterecektir (deneyin!). bu yüzden sadece bir liste olması durumunda teftişe ihtiyaç duyulmasına rağmen teftişi kullanmaya mecburuz, aksi takdirde çoğu zaman buna ihtiyacımız yoktur. typeof, if (O (n)) ifadelerini sözlük aramalarına (O (1)) çevirmemizi sağlar.
Dmitry

1
@Bu tür kullanım için dmitry Elixir protokolleri yararlı olacaktır. elixir-lang.org/getting-started/protocols.htmlPrintable Örneğin, tamsayı listeleri gibi yazdırma davranışını saran ve değiştiren kendi protokolünüzü uygulayabilirsiniz . Sadece Erlang koduyla kullanmadığınızdan emin olun - ya da mesajlar yerine neden tamsayıların listesini gördüğünüzü merak ederek kafanızı kaşıracaksınız.
Matt Jadczak

168

iİksir 1.2'den başlayarak, iex'te herhangi bir İksir değişkeninin türünü ve daha fazlasını listeleyecek bir komut vardır .

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

iKomutun koduna bakarsanız, bunun bir Protokol yoluyla uygulandığını göreceksiniz.

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

Elixir'de herhangi bir Veri türü için bir işlev uygulamak istiyorsanız, bunun yolu, bir Protokol tanımlamak ve işlevin çalışmasını istediğiniz tüm veri türleri için Protokolün uygulanmasıdır. Ne yazık ki, korumalarda Protokol işlevini kullanamazsınız. Bununla birlikte, basit bir "tip" protokolün uygulanması çok basit olacaktır.


1
2019 yılında bu bir hata döndürür undefined function i/1- bilgi / 1 için aynı
krivar

1
Bu hala İksir 1.8.1'de çalışıyor. Yüklü çok eski bir iksir sürümünüz olmalıdır.
Fred Wonder Magic Dog

2
@krivar @ fred-the-magic-wonder-dog ikiniz de haklısınız :). &i/1bir işlevdir IEx.Helpers. &IEx.Helpers.i/1Vanilya Elixir'inize koyarsanız , uygulama olarak CompileErrordahil etmediğiniz sürece bir . :iexmix.exs
popedotninja

39

Ayrıca hata ayıklama amacıyla, iex'te değilseniz, doğrudan arayabilirsiniz:

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

1
Günlüğünüzde görmek istiyorsanız IO.inspect'i ekleyin (IEx.Info.info (5))
Guillaume

24

Diğer bir yaklaşım kalıp eşleştirme kullanmaktır. Diyelim ki bir %DateTime{}yapı kullanan Timex'i kullandınız ve bir elemanın bir tane olup olmadığını görmek istiyorsunuz. Yöntemde desen eşleşmesini kullanarak bir eşleşme bulabilirsiniz.

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

def is_a_datetime?(_) do
  false
end

1
ya da, kabul edilen cevabın belirttiği, ancak vurgulamadığı gibi: »Buna göre hareket etmek için genellikle bir değişkenin türünü bilmek istersiniz«. İksir'de buna göre kalıp eşleşmesi ile hareket edersiniz, switch/ ile değil case.
mariotomo

18

Bunu umarım gerçekten aklı başında bir versiyonunu bulmak için buraya bırakacağım. Şu anda bu google geliyor için iyi bir cevap var ...

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

Bütünlük adına test senaryoları:

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

İşte protokollerle bir çözüm; Daha hızlı olup olmadıklarından emin değilim (umarım tüm türler üzerinde bir döngü yapmazlar), ama oldukça çirkin (ve kırılgan; temel bir tür eklerse veya yeniden adlandırırlarsa, onu kıracaktır).

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

Gerçekten bir "tip" denetleyici istiyorsanız, filozofun taş orgındaki araçları kullanarak kolayca bir tane inşa edersiniz. github.com/philosophers-stone . Fenetik hala ilk günlerde, ama bunu ve çok daha fazlasını yapabilir.
Fred Wonder Wonder Dog

dış bağımlılığa kolayca bağlanabiliyor muyum? bu, arkadaşlarımla kod paylaşma yeteneğimi nasıl geliştirecek? Bu 2 probleme giden bir yoldur.
Dmitry

@Aks düzenlemesi için teşekkürler; Aslında şimdi 4 boşluğa geri dönebilirim ^ _ ^
Dmitry

15

Kodu https://elixirforum.com/t/just-created-a-typeof-module/2583/5 :) adresinden yapıştırıyorum.

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

Alıntı akıllı kullanımı! İksir kodunu ne kadar çok görürsem bana Perl'i hatırlatıyor; Bu yapı qw // 'ya çok benzemektedir. Perl'in Lisplike alıntısını simüle etmek için akıllı bir mekanizması olup olmadığını merak ediyorum.
Dmitry

Teklifin nasıl çalıştığını merak ediyorum; normal bir ifade ön işlemcisi kullanılarak taklit edilebilir veya makro genişletme yapmak için kodun tamamında bir ayrıştırıcı yürüyüşü gerektiriyor mu?
Dmitry

1

Bir durumla karşılaştım, parametrenin belirli tipte olması gerektiğini kontrol etmeliyim. Belki daha iyi bir şekilde aktif olabilir.

Bunun gibi:

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

Kullanımı:

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

1

Çünkü kimse bundan bahsetmediği için

IO.inspect/1

Nesneyi konsolize etmek için çıktılar ... JSON.stringify ile neredeyse aynı

Bir nesnenin testte neye benzediğini anlamanız için çok yararlı.


4
Soruya bir cevap değil, hatta yakın değil
LowFieldTheory
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.