Elixir - Hızlı Kılavuz

Elixir, ölçeklenebilir ve bakımı yapılabilir uygulamalar oluşturmak için tasarlanmış dinamik, işlevsel bir dildir. Düşük gecikmeli, dağıtılmış ve hataya dayanıklı sistemleri çalıştırmasıyla bilinen Erlang VM'den yararlanırken, aynı zamanda web geliştirme ve gömülü yazılım alanında başarıyla kullanılmaktadır.

Elixir, Erlang ve Erlang VM üzerine kurulu işlevsel, dinamik bir dildir. Erlang, 1986 yılında Ericsson tarafından dağıtım, hata toleransı ve eşzamanlılık gibi telefon sorunlarını çözmeye yardımcı olmak için yazılmış bir dildir. José Valim tarafından yazılan Elixir, Erlang'ı genişletir ve Erlang VM'ye daha dostane bir sözdizimi sağlar. Bunu Erlang ile aynı seviyede tutarken yapar.

İksirin Özellikleri

Şimdi Elixir'in birkaç önemli özelliğini tartışalım -

  • Scalability - Tüm Elixir kodu, izole edilmiş ve mesajlar yoluyla bilgi alışverişi yapan hafif süreçler içinde çalışır.

  • Fault Tolerance- Elixir, işler ters gittiğinde sisteminizin parçalarını nasıl yeniden başlatacağınızı ve çalışması garanti edilen bilinen bir başlangıç ​​durumuna nasıl geri döneceğinizi açıklayan denetçiler sağlar. Bu, uygulamanızın / platformunuzun asla kapanmamasını sağlar.

  • Functional Programming - İşlevsel programlama, geliştiricilerin kısa, hızlı ve sürdürülebilir kod yazmalarına yardımcı olan bir kodlama stilini destekler.

  • Build tools- Elixir, bir dizi geliştirme aracıyla birlikte gönderilir. Mix, proje oluşturmayı, görevleri yönetmeyi, testleri çalıştırmayı vb. Kolaylaştıran böyle bir araçtır. Ayrıca kendi paket yöneticisi de vardır - Hex.

  • Erlang Compatibility - Elixir, geliştiricilere Erlang'ın ekosistemine tam erişim sağlayan Erlang VM'de çalışır.

Elixir'i çalıştırmak için sisteminizde yerel olarak kurmanız gerekir.

Elixir'i yüklemek için önce Erlang'a ihtiyacınız olacak. Bazı platformlarda Elixir paketleri Erlang ile birlikte gelir.

Elixir Kurulumu

Şimdi Elixir'in farklı İşletim Sistemlerinde kurulumunu anlayalım.

Windows kurulumu

Elixir'i Windows'a yüklemek için, aşağıdaki adresten yükleyiciyi indirin: https://repo.hex.pm/elixirwebsetup.exe ve sadece tıklayın Nexttüm adımlar boyunca ilerlemek için. Yerel sisteminizde ona sahip olacaksınız.

Kurulum sırasında herhangi bir sorun yaşarsanız, daha fazla bilgi için bu sayfayı kontrol edebilirsiniz .

Mac Kurulumu

Homebrew yüklediyseniz, en son sürüm olduğundan emin olun. Güncelleme için aşağıdaki komutu kullanın -

brew update

Şimdi, Elixir'i aşağıda verilen komutu kullanarak yükleyin -

brew install elixir

Ubuntu / Debian Kurulumu

Elixir'i bir Ubuntu / Debian kurulumuna kurma adımları aşağıdaki gibidir -

Erlang Solutions deposu ekle -

wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo 
dpkg -i erlang-solutions_1.0_all.deb 
sudo apt-get update

Erlang / OTP platformunu ve tüm uygulamalarını yükleyin -

sudo apt-get install esl-erlang

Elixir'i yükleyin -

sudo apt-get install elixir

Diğer Linux Dağıtımları

Başka bir Linux dağıtımınız varsa, yerel sisteminizde iksiri kurmak için lütfen bu sayfayı ziyaret edin .

Kurulumu Test Etme

Elixir kurulumunu sisteminizde test etmek için terminalinizi açın ve içine iex girin. Etkileşimli iksir kabuğunu aşağıdaki gibi açacaktır -

Erlang/OTP 19 [erts-8.0] [source-6dc93c1] [64-bit] 
[smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]  

Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help) 
iex(1)>

Elixir artık sisteminizde başarıyla kuruldu.

Alışılmış 'Merhaba Dünya' programı ile başlayacağız.

Elixir etkileşimli kabuğunu başlatmak için aşağıdaki komutu girin.

iex

Kabuk başladıktan sonra, IO.putsdizeyi konsol çıktısına "koymak" için işlev. İksir kabuğunuza aşağıdakileri girin -

IO.puts "Hello world"

Bu eğiticide, Elixir kodunu uzantıya sahip bir dosyada saklayacağımız Elixir komut dosyası modunu kullanacağız. .ex. Şimdi yukarıdaki kodu,test.exdosya. Sonraki adımda, bunu kullanarak yürüteceğizelixirc-

IO.puts "Hello world"

Şimdi yukarıdaki programı aşağıdaki gibi çalıştırmayı deneyelim -

$elixirc test.ex

Yukarıdaki program aşağıdaki sonucu üretir -

Hello World

Burada bir fonksiyon diyoruz IO.putsçıktı olarak konsolumuza bir dize oluşturmak için. Bu fonksiyon aynı zamanda C, C ++, Java, vb. De yaptığımız şekilde adlandırılabilir ve fonksiyon isminin ardından parantez içinde argümanlar sağlar -

IO.puts("Hello world")

Yorumlar

Tek satırlı yorumlar bir '#' sembolüyle başlar. Çok satırlı yorum yoktur, ancak birden çok yorumu yığabilirsiniz. Örneğin -

#This is a comment in Elixir

Satır Sonları

';' Gibi gerekli satır sonları yoktur İksir içinde. Bununla birlikte, aynı satırda ';' kullanarak birden fazla ifade alabiliriz. Örneğin,

IO.puts("Hello"); IO.puts("World!")

Yukarıdaki program aşağıdaki sonucu üretir -

Hello 
World!

Tanımlayıcılar

Değişkenler, işlev adları gibi tanımlayıcılar, bir değişkeni, işlevi vb. Tanımlamak için kullanılır. Elixir'de, tanımlayıcılarınızı daha sonra sayılar, alt çizgiler ve büyük harflerle küçük harflerle başlayarak adlandırabilirsiniz. Bu adlandırma kuralı genellikle snake_case olarak bilinir. Örneğin, Elixir'deki bazı geçerli tanımlayıcılar aşağıdadır -

var1       variable_2      one_M0r3_variable

Unutmayın ki değişkenler baştaki bir alt çizgiyle de adlandırılabilir. Kullanılması amaçlanmayan bir değer, _ veya alt çizgi ile başlayan bir değişkene atanmalıdır -

_some_random_value = 42

Ayrıca iksir, işlevleri modüllere özel kılmak için alt çizgilere dayanır. Bir modülde önünde alt çizgi olan bir işlevi adlandırırsanız ve bu modülü içe aktarırsanız, bu işlev içe aktarılmaz.

İlerideki bölümlerde tartışacağımız Elixir'de işlev adlandırma ile ilgili daha birçok karmaşıklık var.

Ayrılmış kelimeler

Aşağıdaki sözcükler ayrılmıştır ve değişken, modül veya işlev adı olarak kullanılamaz.

after     and     catch     do     inbits     inlist     nil     else     end 
not     or     false     fn     in     rescue     true     when     xor 
__MODULE__    __FILE__    __DIR__    __ENV__    __CALLER__

Herhangi bir dili kullanmak için, dilin desteklediği temel veri türlerini anlamanız gerekir. Bu bölümde, iksir dili tarafından desteklenen 7 temel veri türünü tartışacağız: tamsayılar, kayan değerler, Boole'lar, atomlar, dizeler, listeler ve tuplelar.

Sayısal Türler

Elixir, diğer herhangi bir programlama dili gibi, hem tam sayıları hem de kayan sayıları destekler. İksir kabuğunuzu açarsanız ve girdi olarak herhangi bir tamsayı veya kayar sayı girerseniz, değerini döndürür. Örneğin,

42

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

42

Sayıları sekizlik, onaltılık ve ikili tabanda da tanımlayabilirsiniz.

Sekizli

Sekizlik tabanda bir sayının önüne '0o' koyun. Örneğin, sekizlik tabanda 0o52, onluk tabanda 42'ye eşittir.

Onaltılık

Bir sayıyı ondalık tabanda tanımlamak için, önüne '0x' koyun. Örneğin, onaltılık sayıdaki 0xF1, ondalık sayıdaki 241'e eşittir.

İkili

İkili tabanda bir sayı tanımlamak için, önüne '0b' koyun. Örneğin, ikilik tabanda 0b1101, ondalık tabanda 13'e eşdeğerdir.

Elixir, kayan nokta sayıları için 64bit çift hassasiyeti destekler. Ayrıca bir üs alma stili kullanılarak da tanımlanabilirler. Örneğin 10145230000, 1.014523e10 olarak yazılabilir.

Atomlar

Atomlar, adı değerleri olan sabitlerdir. Renk (:) sembolü kullanılarak oluşturulabilirler. Örneğin,

:hello

Boole'lar

Elixir destekler true ve falseBooleans olarak. Bu değerlerin ikisi de aslında atomlara bağlıdır: sırasıyla doğru ve: yanlış.

Teller

Elixir'deki dizeler çift tırnak arasına eklenir ve UTF-8 ile kodlanır. Birden çok satıra yayılabilirler ve enterpolasyonlar içerebilirler. Bir dizeyi tanımlamak için çift tırnak içine almanız yeterlidir -

"Hello world"

Çok satırlı dizeleri tanımlamak için, üçlü çift tırnaklı python'a benzer bir sözdizimi kullanıyoruz -

"""
Hello
World!
"""

Dizeler bölümünde, dizeler, ikili dosyalar ve karakter listeleri (dizgilere benzer) hakkında derinlemesine bilgi edineceğiz.

İkili dosyalar

İkili dosyalar, << >> içine alınmış ve virgülle ayrılmış bayt dizileridir. Örneğin,

<< 65, 68, 75>>

İkili dosyalar, eğer varsa, çoğunlukla bit ve bayt ile ilgili verileri işlemek için kullanılır. Varsayılan olarak, her bir değerde 0 ila 255 saklayabilirler. Bu boyut sınırı, bu değeri depolamak için kaç bit gerektiğini söyleyen boyut işlevi kullanılarak artırılabilir. Örneğin,

<<65, 255, 289::size(15)>>

Listeler

Elixir, bir değer listesi belirtmek için köşeli parantez kullanır. Değerler herhangi bir türde olabilir. Örneğin,

[1, "Hello", :an_atom, true]

Listeler, sırasıyla listenin başını ve sonunu döndüren hd ve tl adlı listenin baş ve kuyruğu için dahili işlevlerle birlikte gelir. Bazen bir liste oluşturduğunuzda, bir karakter listesi döndürür. Bunun nedeni, iksir yazdırılabilir ASCII karakterlerinin bir listesini gördüğünde, bunu bir karakter listesi olarak yazdırmasıdır. Lütfen dizelerin ve karakter listelerinin eşit olmadığını unutmayın. Listeleri daha sonraki bölümlerde tartışacağız.

Tuples

Elixir, tupleları tanımlamak için küme parantezleri kullanır. Listeler gibi, tuplelar da herhangi bir değeri tutabilir.

{ 1, "Hello", :an_atom, true

Burada bir soru ortaya çıkıyor - neden ikisini de lists ve tuplesikisi de aynı şekilde çalıştığında? Farklı uygulamaları var.

  • Listeler aslında bağlantılı listeler olarak saklanır, bu nedenle listelerde eklemeler, silmeler çok hızlıdır.

  • Öte yandan, kayıtlar bitişik bellek bloğunda depolanır, bu da bunlara erişimi daha hızlı hale getirir, ancak ekleme ve silme işlemlerine ek bir maliyet ekler.

Bir değişken, programlarımızın değiştirebileceği adlandırılmış depolama alanı sağlar. Elixir'deki her değişkenin, değişkenin belleğinin boyutunu ve düzenini belirleyen belirli bir türü vardır; o hafızada saklanabilecek değerlerin aralığı; ve değişkene uygulanabilecek işlemler kümesi.

Değişken Türleri

Elixir, aşağıdaki temel değişken türlerini destekler.

Tamsayı

Bunlar Tamsayılar için kullanılır. 32 bit mimaride 32 bit ve 64 bit mimaride 64 bit boyutundadırlar. Tamsayılar her zaman iksir olarak işaretlenir. Bir tamsayı, sınırının üzerinde boyut olarak genişlemeye başlarsa, iksir onu, belleğe hangisi sığabilirse, 3 ila n kelime aralığında bellek alan bir Büyük Tamsayı'ya dönüştürür.

Yüzer

Şamandıralar, iksirde 64-bit hassasiyete sahiptir. Hafıza açısından da tamsayı gibidirler. Bir kayan nokta tanımlanırken üstel gösterim kullanılabilir.

Boole

Doğru veya yanlış olan 2 değeri alabilirler.

Teller

Dizeler iksirle kodlanmış utf-8'dir. Dizeleri işlemek için programcıya birçok işlevsellik sağlayan bir dizeler modülü vardır.

Anonim İşlevler / Lambdalar

Bunlar, tanımlanabilen ve bir değişkene atanabilen ve daha sonra bu işlevi çağırmak için kullanılabilen işlevlerdir.

Koleksiyonlar

Elixir'de pek çok koleksiyon türü mevcuttur. Bunlardan bazıları Listeler, Tuplelar, Haritalar, İkili dosyalar, vs.'dir. Bunlar sonraki bölümlerde tartışılacaktır.

Değişken Beyanı

Değişken bildirimi, yorumlayıcıya değişken için depolamanın nerede ve ne kadar oluşturulacağını söyler. Elixir, sadece bir değişken tanımlamamıza izin vermiyor. Bir değişken bildirilmeli ve aynı zamanda bir değer atanmalıdır. Örneğin, life adında bir değişken oluşturmak ve ona bir 42 değeri atamak için aşağıdakileri yapıyoruz -

life = 42

Bu, ömür değişkenini 42 değerine bağlayacaktır . Bu değişkene yeni bir değer atamak istiyorsak, bunu yukarıdaki ile aynı sözdizimini kullanarak yapabiliriz, yani,

life = "Hello world"

Değişken Adlandırma

Değişkenleri adlandırmak için aşağıdaki snake_caseİksir'deki kural, yani tüm değişkenler küçük harfle başlamalı, ardından 0 veya daha fazla harf (hem büyük hem de küçük harf) ve sonunda isteğe bağlı bir '?' VEYA '!'.

Değişken isimleri ayrıca bir ön alt çizgi ile de başlatılabilir, ancak bu sadece değişken yok sayılırken kullanılmalıdır, yani bu değişken tekrar kullanılmayacak, ancak bir şeye atanması gerekir.

Değişkenleri Yazdırma

Etkileşimli kabukta, yalnızca değişken adını girerseniz değişkenler yazdırılır. Örneğin, bir değişken oluşturursanız -

life = 42

Ve kabuğunuza 'yaşam' girin, çıktıyı şu şekilde alacaksınız -

42

Ancak konsola bir değişken çıktı vermek istiyorsanız (bir dosyadan harici bir komut dosyası çalıştırırken), değişkeni giriş olarak sağlamanız gerekir. IO.puts işlev -

life = 42  
IO.puts life

veya

life = 42 
IO.puts(life)

Bu size aşağıdaki çıktıyı verecektir -

42

Operatör, derleyiciye belirli matematiksel veya mantıksal işlemleri gerçekleştirmesini söyleyen bir semboldür. Elixir tarafından sağlanan çok sayıda operatör vardır. Aşağıdaki kategorilere ayrılırlar -

  • Aritmetik operatörler
  • Karşılaştırma operatörleri
  • Boole operatörleri
  • Çeşitli operatörler

Aritmetik operatörler

Aşağıdaki tablo Elixir dili tarafından desteklenen tüm aritmetik işleçleri göstermektedir. Değişken varsayA 10 ve değişken tutar B 20 tutar, sonra -

Örnekleri Göster

Şebeke Açıklama Misal
+ 2 numara ekler. A + B 30 verecek
- İkinci sayıyı ilkinden çıkarır. AB -10 verecek
* İki sayıyı çarpar. A * B 200 verecek
/ İlk sayıyı ikinciden böler. Bu sayıları kayan sayılara dönüştürür ve bir kayan sonuç verir A / B 0,5 verecektir.
div Bu fonksiyon bölme ile ilgili bölümü elde etmek için kullanılır. div (10,20) 0 verir
rem Bu işlev, bölmede kalanı elde etmek için kullanılır. rem (A, B) 10 verecektir

Karşılaştırma Operatörleri

Elixir'deki karşılaştırma operatörleri çoğunlukla diğer birçok dilde sağlananlarda ortaktır. Aşağıdaki tablo Elixir'deki karşılaştırma operatörlerini özetlemektedir. Değişken varsayA 10 ve değişken tutar B 20 tutar, sonra -

Örnekleri Göster

Şebeke Açıklama Misal
== Soldaki değerin sağdaki değere eşit olup olmadığını kontrol eder (Tür, aynı türde değilse değerleri çevirir). A == B yanlış verir
! = Soldaki değerin sağdaki değere eşit olup olmadığını kontrol eder. A! = B doğru verecektir
=== Soldaki değer türünün sağdaki değer türüne eşit olup olmadığını kontrol eder, evet ise değer için aynısını kontrol edin. A === B yanlış verir
! == Yukarıdakinin aynısı ancak eşitlik yerine eşitsizliği kontrol ediyor. A! == B doğru verecektir
> Soldaki işlenenin değerinin sağ işlenenin değerinden büyük olup olmadığını kontrol eder; evet ise, o zaman durum gerçek olur. A> B yanlış verir
< Sol işlenenin değerinin sağ işlenenin değerinden küçük olup olmadığını kontrol eder; evet ise, o zaman durum gerçek olur. A <B doğru verecektir
> = Sol işlenenin değerinin sağ işlenenin değerinden büyük veya ona eşit olup olmadığını kontrol eder; evet ise, o zaman durum gerçek olur. A> = B yanlış verir
<= Sol işlenenin değerinin sağ işlenenin değerinden küçük veya ona eşit olup olmadığını kontrol eder; evet ise, o zaman durum gerçek olur. A <= B doğru verecektir

Mantıksal operatörler

Elixir 6 mantıksal operatör sağlar: ve, veya, değil, &&, || ve !. İlk üç,and or notkatı Boole operatörleri, yani ilk argümanlarının bir Boolean olmasını bekledikleri anlamına gelir. Boolean olmayan bağımsız değişken bir hataya neden olur. Sonraki üçte,&&, || and !katı değildir, ilk değeri kesinlikle bir boole olarak almamızı gerektirmez. Katı meslektaşları ile aynı şekilde çalışırlar. Değişken varsayA doğru ve değişken tutar B 20 tutar, sonra -

Örnekleri Göster

Şebeke Açıklama Misal
ve Sağlanan her iki değerin doğru olup olmadığını kontrol eder, evet ise ikinci değişkenin değerini döndürür. (Mantıksal ve). A ve B 20 verecek
veya Sağlanan değerlerden herhangi birinin doğru olup olmadığını kontrol eder. Hangi değerin doğru olduğunu döndürür. Else yanlış döndürür. (Mantıksal veya). A veya B doğru verecek
değil Verilen girdinin değerini tersine çeviren tekli operatör. A değil yanlış verir
&& Katı olmayan and. Aynı şekilde çalışırand ancak ilk bağımsız değişkenin bir Boolean olmasını beklemiyor. B && A 20 verecek
|| Katı olmayan or. Aynı şekilde çalışıror ancak ilk bağımsız değişkenin bir Boolean olmasını beklemiyor. B || A doğru verecek
! Katı olmayan not. Aynı şekilde çalışırnot ancak argümanın bir Boolean olmasını beklemez. ! A yanlış verecek

NOTE −ve , veya , && ve || || kısa devre operatörleridir. Bu, ilk argümanınandyanlışsa, ikincisini daha fazla kontrol etmeyecektir. Ve eğer ilk argümanordoğruysa, ikincisini kontrol etmeyecektir. Örneğin,

false and raise("An error")  
#This won't raise an error as raise function wont get executed because of short
#circuiting nature of and operator

Bitsel Operatörler

Bitsel operatörler bitler üzerinde çalışırlar ve bit işlem gerçekleştirirler. Elixir, paketin bir parçası olarak bitsel modüller sağlarBitwise, bu yüzden bunları kullanmak için bitsel modülü kullanmanız gerekir . Kullanmak için kabuğunuza aşağıdaki komutu girin -

use Bitwise

Aşağıdaki örnekler için A'nın 5 ve B'nin 6 olduğunu varsayın -

Örnekleri Göster

Şebeke Açıklama Misal
&&& Bitsel ve operatör, her iki işlenende de mevcutsa, sonuç için bir bit kopyalar. A &&& B 4 verir
||| Bitsel veya işleç, işlenenlerden herhangi birinde mevcutsa sonuca bir bit kopyalar. Bir ||| B 7 verecek
>>> Bitsel sağa kaydırma operatörü, ilk işlenen bitlerini ikinci işlenende belirtilen sayı kadar sağa kaydırır. A >>> B 0 verir
<<< Bitsel sola kaydırma operatörü, birinci işlenen bitlerini ikinci işlenende belirtilen sayı kadar sola kaydırır. A <<< B 320 verir
^^ ^ Bitsel XOR operatörü, yalnızca her iki işlenende farklıysa sonuç için bir bit kopyalar. A ^ ^ B 3 verir
~~~ Tekli bitsel değil, verilen sayıdaki bitleri ters çevirmez. ~~~ A -6 verir

Çeşitli Operatörler

Elixir, yukarıdaki operatörler dışında bir dizi başka operatörü de sağlar. Concatenation Operator, Match Operator, Pin Operator, Pipe Operator, String Match Operator, Code Point Operator, Capture Operator, Ternary Operator bu onu oldukça güçlü bir dil yapar.

Örnekleri Göster

Örüntü eşleştirme, Elixir'in Erlang'dan miras aldığı bir tekniktir. Listeler, tuplelar, haritalar vb. Gibi karmaşık veri yapılarından daha basit alt yapıları çıkarmamızı sağlayan çok güçlü bir tekniktir.

Bir maçın 2 ana bölümü vardır, left ve bir rightyan. Sağ taraf, her türden bir veri yapısıdır. Sol taraf, sağ taraftaki veri yapısını eşleştirmeye ve soldaki herhangi bir değişkeni sağdaki ilgili altyapıya bağlamaya çalışır. Bir eşleşme bulunmazsa, operatör bir hata verir.

En basit eşleşme, soldaki tek bir değişkendir ve sağdaki herhangi bir veri yapısıdır. This variable will match anything. Örneğin,

x = 12
x = "Hello"
IO.puts(x)

Bir alt yapıyı yakalayabilmek için değişkenleri bir yapının içine yerleştirebilirsiniz. Örneğin,

[var_1, _unused_var, var_2] = [{"First variable"}, 25, "Second variable" ]
IO.puts(var_1)
IO.puts(var_2)

Bu değerleri depolayacak, {"First variable"}içinde VAR_1 ve"Second variable"içinde VAR_2 . Ayrıca özel bir_ tam olarak diğer değişkenler gibi çalışan ancak iksire söyleyen değişken (veya '_' ile başlayan değişkenler), "Make sure something is here, but I don't care exactly what it is.". Önceki örnekte, _unused_var böyle bir değişkendi.

Bu tekniği kullanarak daha karmaşık kalıpları eşleştirebiliriz. İçinexample Listede bulunan bir listenin içindeki bir sayıyı açmak ve almak istiyorsanız, aşağıdaki komutu kullanabilirsiniz -

[_, [_, {a}]] = ["Random string", [:an_atom, {24}]]
IO.puts(a)

Yukarıdaki program aşağıdaki sonucu üretir -

24

Bu bağlayacak a '_' kullandığımız için diğer değerler göz ardı edilir.

Örüntü eşleştirmede, üzerinde bir değişken kullanırsak rightdeğeri kullanılır. Soldaki bir değişkenin değerini kullanmak istiyorsanız, pin operatörünü kullanmanız gerekir.

Örneğin, 25 değerine sahip bir "a" değişkeniniz varsa ve bunu 25 değerine sahip başka bir "b" değişkeniyle eşleştirmek istiyorsanız, o zaman şunu girmeniz gerekir -

a = 25
b = 25
^a = b

Son satır şu anki değerle eşleşiyor a, atamak yerine, değerine b. Eşleşmeyen bir sol ve sağ taraf kümesine sahipsek, eşleştirme operatörü bir hata verir. Örneğin, bir demeti bir listeyle veya 2 boyutundaki bir listeyi 3 boyutundaki bir listeyle eşleştirmeye çalışırsak, bir hata görüntülenir.

Karar verme yapıları, programcının, program tarafından değerlendirilecek veya test edilecek bir veya daha fazla koşulu, koşulun geçerli olduğu belirlenirse yürütülecek bir ifade veya ifadeyle birlikte belirtmesini gerektirir. trueve isteğe bağlı olarak, koşul olarak belirlenirse yürütülecek diğer ifadeler false.

Aşağıda, programlama dilinin çoğunda bulunan tipik bir karar verme yapısının genelidir -

Elixir, diğer birçok programlama dili gibi if / else koşullu yapılar sağlar. Ayrıca bircondBulduğu ilk gerçek değeri çağıran ifade. Durum, programın akışını kontrol etmek için model eşleştirmeyi kullanan başka bir kontrol akışı ifadesidir. Onlara derinlemesine bakalım.

Elixir, aşağıdaki türden karar verme beyanları sağlar. Ayrıntılarını kontrol etmek için aşağıdaki bağlantıları tıklayın.

Sr.No. Açıklama ve Açıklama
1 eğer ifadesi

Bir if ifadesi, bir Boole ifadesinin ardından gelen do, bir veya daha fazla çalıştırılabilir ifade ve son olarak bir endanahtar kelime. İf deyimi yalnızca Boole koşulu doğru olarak değerlendirilirse çalıştırılır.

2 if..else ifadesi

Bir if ifadesinin ardından, Boolean ifadesi yanlış olduğunda yürütülen isteğe bağlı bir else ifadesi (do..end bloğu içinde) gelebilir.

3 ifade olmadıkça

If ifadesiyle aynı gövdeye sahip olmayan bir ifade. İfadesindeki kod, yalnızca belirtilen koşul yanlış olduğunda yürütülür.

4 sürece..else ifadesi

If..else ifadesiyle aynı gövdeye sahip bir unt..else ifadesi vardır. İfadesindeki kod, yalnızca belirtilen koşul yanlış olduğunda yürütülür.

5 koşul

Kodu birkaç koşul temelinde yürütmek istediğimiz yerde bir koşul ifadesi kullanılır. Bu, diğer birçok programlama dilinde if ... else if… .else yapısı gibi çalışır.

6 durum

Case ifadesi, zorunlu dillerde switch ifadesinin yerini alacak şekilde düşünülebilir. Case bir değişken / literal alır ve ona farklı durumlarda desen eşleştirme uygular. Herhangi bir durum eşleşirse, Elixir bu vakayla ilişkili kodu yürütür ve case ifadesinden çıkar.

Elixir'deki dizeler çift tırnak arasına eklenir ve UTF-8 ile kodlanır. Varsayılan dizelerin ASCII olarak kodlandığı ve yalnızca 256 farklı karakterin mümkün olduğu C ve C ++ 'dan farklı olarak, UTF-8 1.112.064 kod noktasından oluşur. Bu, UTF-8 kodlamasının bu birçok farklı olası karakterden oluştuğu anlamına gelir. Dizeler utf-8 kullandığından ö, ł vb. Semboller de kullanabiliriz.

Bir Dize Oluşturun

Bir dizge değişkeni oluşturmak için, bir değişkene bir dizge atamanız yeterlidir -

str = "Hello world"

Bunu konsolunuza yazdırmak için, IO.puts fonksiyon ve ona str değişkenini iletin -

str = str = "Hello world" 
IO.puts(str)

Yukarıdaki program aşağıdaki sonucu üretir -

Hello World

Boş Dizeler

Dize değişmezini kullanarak boş bir dize oluşturabilirsiniz, "". Örneğin,

a = ""
if String.length(a) === 0 do
   IO.puts("a is an empty string")
end

Yukarıdaki program aşağıdaki sonucu verir.

a is an empty string

Dize Enterpolasyonu

Dize enterpolasyonu, değerlerini bir dize değişmezinin içine dahil ederek sabitlerin, değişkenlerin, değişmez değerlerin ve ifadelerin bir karışımından yeni bir String değeri oluşturmanın bir yoludur. Elixir, bir dizgede bir değişkeni yazarken kullanmak için dize enterpolasyonunu destekler, onu küme parantezleriyle sarın ve küme parantezlerinin başına bir'#' işaret.

Örneğin,

x = "Apocalypse" 
y = "X-men #{x}"
IO.puts(y)

Bu, x'in değerini alacak ve onu y yerine koyacaktır. Yukarıdaki kod aşağıdaki sonucu oluşturacaktır -

X-men Apocalypse

Dize Birleştirme

Önceki bölümlerde String birleştirme kullanımını zaten görmüştük. Elixir'deki dizeleri birleştirmek için '<>' operatörü kullanılır. 2 dizeyi birleştirmek için,

x = "Dark"
y = "Knight"
z = x <> " " <> y
IO.puts(z)

Yukarıdaki kod aşağıdaki sonucu üretir -

Dark Knight

IP uzunluğu

İpin uzunluğunu elde etmek için, String.lengthişlevi. Dizeyi bir parametre olarak iletin ve size boyutunu gösterecektir. Örneğin,

IO.puts(String.length("Hello"))

Yukarıdaki programı çalıştırırken, aşağıdaki sonucu verir -

5

Bir Dizeyi Ters Çevirme

Bir dizeyi ters çevirmek için onu String.reverse işlevine iletin. Örneğin,

IO.puts(String.reverse("Elixir"))

Yukarıdaki program aşağıdaki sonucu üretir -

rixilE

Dize Karşılaştırması

2 dizgiyi karşılaştırmak için == veya === operatörlerini kullanabiliriz. Örneğin,

var_1 = "Hello world"
var_2 = "Hello Elixir"
if var_1 === var_2 do
   IO.puts("#{var_1} and #{var_2} are the same")
else
   IO.puts("#{var_1} and #{var_2} are not the same")
end

Yukarıdaki program aşağıdaki sonucu üretir -

Hello world and Hello elixir are not the same.

Dize Eşleştirme

= ~ Dizge eşleştirme operatörünün kullanımını daha önce görmüştük. Bir dizenin bir normal ifadeyle eşleşip eşleşmediğini kontrol etmek için, dize eşleştirme operatörünü veya String.match? işlevi. Örneğin,

IO.puts(String.match?("foo", ~r/foo/))
IO.puts(String.match?("bar", ~r/foo/))

Yukarıdaki program aşağıdaki sonucu üretir -

true 
false

Aynı şey = ~ operatörü kullanılarak da elde edilebilir. Örneğin,

IO.puts("foo" =~ ~r/foo/)

Yukarıdaki program aşağıdaki sonucu üretir -

true

String Fonksiyonları

Elixir, dizelerle ilgili çok sayıda işlevi destekler, en çok kullanılanlardan bazıları aşağıdaki tabloda listelenmiştir.

Sr.No. İşlev ve Amacı
1

at(string, position)

Grapheme'yi verilen utf8 dizesinin konumunda döndürür. Konum dize uzunluğundan büyükse, sıfır döndürür

2

capitalize(string)

Verilen dizedeki ilk karakteri büyük harfe ve kalanı küçük harfe dönüştürür

3

contains?(string, contents)

Dizenin verilen içeriklerden herhangi birini içerip içermediğini kontrol eder

4

downcase(string)

Verilen dizedeki tüm karakterleri küçük harfe dönüştürür

5

ends_with?(string, suffixes)

Dize, verilen son eklerden herhangi biriyle biterse doğru döndürür

6

first(string)

Bir utf8 dizesinden ilk grafemeyi döndürür, dize boşsa nil

7

last(string)

Bir utf8 dizesinden son grafemeyi döndürür, dize boşsa nil

8

replace(subject, pattern, replacement, options \\ [])

Konudaki desen oluşumlarını değiştirme ile değiştirerek oluşturulan yeni bir dize döndürür

9

slice(string, start, len)

Göreli konum başlangıcında başlayan ve uzunluk uzunluğu olan bir alt dize döndürür

10

split(string)

Bir dizeyi her Unicode boşluk oluşumunda alt dizelere böler ve baştaki ve sondaki boşluklar yoksayılır. Beyaz boşluk grupları, tek bir oluşum olarak kabul edilir. Bölünemez boşlukta bölünmeler oluşmaz

11

upcase(string)

Verilen dizedeki tüm karakterleri büyük harfe dönüştürür

İkili dosyalar

İkili, sadece bir bayt dizisidir. İkili dosyalar kullanılarak tanımlanır<< >>. Örneğin:

<< 0, 1, 2, 3 >>

Elbette bu baytlar, onları geçerli bir dizge yapmayan bir sırada bile herhangi bir şekilde organize edilebilir. Örneğin,

<< 239, 191, 191 >>

Dizeler aynı zamanda ikilidir. Ve dize birleştirme operatörü<> aslında bir İkili birleştirme operatörüdür:

IO.puts(<< 0, 1 >> <> << 2, 3 >>)

Yukarıdaki kod aşağıdaki sonucu üretir -

<< 0, 1, 2, 3 >>

Ł karakterine dikkat edin. Bu, utf-8 kodlu olduğundan, bu karakter temsili 2 bayt yer kaplar.

İkili dosyada gösterilen her sayının bir bayt olması amaçlandığından, bu değer 255'ten yükseldiğinde kesilir. Bunu önlemek için, bu sayının kaç bit almasını istediğimizi belirtmek için boyut değiştirici kullanıyoruz. Örneğin -

IO.puts(<< 256 >>) # truncated, it'll print << 0 >>
IO.puts(<< 256 :: size(16) >>) #Takes 16 bits/2 bytes, will print << 1, 0 >>

Yukarıdaki program aşağıdaki sonucu üretecektir -

<< 0 >>
<< 1, 0 >>

Ayrıca utf8 değiştiricisini de kullanabiliriz, eğer bir karakter kod noktasıysa, çıktıda üretilecektir; başka baytlar -

IO.puts(<< 256 :: utf8 >>)

Yukarıdaki program aşağıdaki sonucu üretir -

Ā

Ayrıca adında bir fonksiyonumuz var is_binarybu, belirli bir değişkenin ikili olup olmadığını kontrol eder. Yalnızca 8 bitin katları olarak depolanan değişkenlerin ikili olduğunu unutmayın.

Bit dizeleri

Boyut değiştiriciyi kullanarak bir ikili tanımlar ve 8'in katı olmayan bir değer iletirsek, ikili yerine bir bit dizesi elde ederiz. Örneğin,

bs = << 1 :: size(1) >>
IO.puts(bs)
IO.puts(is_binary(bs))
IO.puts(is_bitstring(bs))

Yukarıdaki program aşağıdaki sonucu üretir -

<< 1::size(1) >>
false
true

Bu, değişkenin bsbir ikili değil, daha çok bir bit dizisidir. İkilinin, bit sayısının 8'e bölünebildiği bir bit dizisi olduğunu da söyleyebiliriz. Örüntü eşleştirme hem ikili dosyalar üzerinde hem de bit dizgilerinde aynı şekilde çalışır.

Bir karakter listesi, bir karakter listesinden başka bir şey değildir. Aynısını anlamak için aşağıdaki programı düşünün.

IO.puts('Hello')
IO.puts(is_list('Hello'))

Yukarıdaki program aşağıdaki sonucu üretir -

Hello
true

Bir karakter listesi, baytları içermek yerine, tek tırnak işaretleri arasındaki karakterlerin kod noktalarını içerir. So while the double-quotes represent a string (i.e. a binary), singlequotes represent a char list (i.e. a list). Karakterlerden herhangi biri ASCII aralığının dışındaysa, IEx'in çıktı olarak yalnızca kod noktaları oluşturacağını unutmayın.

Karakter listeleri çoğunlukla Erlang ile arabirim oluştururken, özellikle ikili dosyaları argüman olarak kabul etmeyen eski kütüphanelerde kullanılır. To_string (char_list) ve to_char_list (string) işlevlerini kullanarak bir char listesini bir dizeye ve geri dönüştürebilirsiniz -

IO.puts(is_list(to_char_list("hełło")))
IO.puts(is_binary(to_string ('hełło')))

Yukarıdaki program aşağıdaki sonucu üretir -

true
true

NOTE - Fonksiyonlar to_string ve to_char_list polimorfiktir, yani atomlar, tamsayılar gibi birden çok girdi türü alabilir ve bunları sırasıyla dizelere ve karakter listelerine dönüştürebilirler.

(Bağlantılı) Listeler

Bağlantılı liste, bellekte farklı konumlarda depolanan ve referanslar kullanılarak izlenen öğelerin heterojen bir listesidir. Bağlantılı listeler, özellikle fonksiyonel programlamada kullanılan veri yapılarıdır.

Elixir, bir değer listesi belirtmek için köşeli parantez kullanır. Değerler herhangi bir türde olabilir -

[1, 2, true, 3]

Elixir yazdırılabilir ASCII numaralarının bir listesini gördüğünde, Elixir bunu bir karakter listesi (kelimenin tam anlamıyla bir karakter listesi) olarak yazdıracaktır. IEx'te bir değer gördüğünüzde ve ne olduğundan emin değilseniz,i hakkında bilgi almak için işlev.

IO.puts([104, 101, 108, 108, 111])

Listedeki yukarıdaki karakterlerin tümü yazdırılabilir. Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

hello

Ayrıca tek tırnak işaretleri kullanarak listeleri tam tersi şekilde de tanımlayabilirsiniz -

IO.puts(is_list('Hello'))

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

true

Tek tırnaklı ve çift tırnaklı temsillerin Elixir'de farklı türlerle temsil edildiklerinden eşdeğer olmadığını unutmayın.

Bir Listenin Uzunluğu

Bir listenin uzunluğunu bulmak için aşağıdaki programdaki gibi uzunluk fonksiyonunu kullanıyoruz -

IO.puts(length([1, 2, :true, "str"]))

Yukarıdaki program aşağıdaki sonucu üretir -

4

Birleştirme ve Çıkarma

Kullanılarak iki liste birleştirilebilir ve çıkarılabilir. ++ ve --operatörler. İşlevleri anlamak için aşağıdaki örneği düşünün.

IO.puts([1, 2, 3] ++ [4, 5, 6])
IO.puts([1, true, 2, false, 3, true] -- [true, false])

Bu size birinci durumda birleştirilmiş bir dize ve ikinci durumda çıkarılmış bir dize verecektir. Yukarıdaki program aşağıdaki sonucu üretir -

[1, 2, 3, 4, 5, 6]
[1, 2, 3, true]

Bir Listenin Başı ve Sonu

Baş, bir listenin ilk öğesidir ve kuyruk, bir listenin geri kalanıdır. Fonksiyonlar ile geri çağrılabilirlerhd ve tl. Bir değişkene bir liste atayalım ve başını ve sonunu alalım.

list = [1, 2, 3]
IO.puts(hd(list))
IO.puts(tl(list))

Bu bize listenin başını ve sonunu çıktı olarak verecektir. Yukarıdaki program aşağıdaki sonucu üretir -

1
[2, 3]

Note - Boş bir listenin başını veya sonunu almak bir hatadır.

Diğer Liste işlevleri

Elixir standart kitaplığı, listelerle uğraşmak için çok sayıda işlev sağlar. Bunlardan bazılarına burada bir göz atacağız. Burada kalanını kontrol edebilirsiniz Liste .

S.no. İşlev Adı ve Açıklaması
1

delete(list, item)

Verilen öğeyi listeden siler. Öğe olmadan bir liste döndürür. Öğe listede birden çok kez geçerse, yalnızca ilk geçtiği yer kaldırılır.

2

delete_at(list, index)

Belirtilen dizindeki değeri kaldırarak yeni bir liste oluşturur. Negatif endeksler, listenin sonundan bir ofseti gösterir. Dizin sınırların dışındaysa, orijinal liste döndürülür.

3

first(list)

Listedeki ilk öğeyi döndürür veya liste boşsa nil'i döndürür.

4

flatten(list)

Verilen iç içe listelerin listesini düzleştirir.

5

insert_at(list, index, value)

Belirtilen dizine eklenen değere sahip bir liste döndürür. Dizinin liste uzunluğunda sınırlandırıldığını unutmayın. Negatif endeksler, listenin sonundan bir ofseti gösterir.

6

last(list)

Listedeki son öğeyi döndürür veya liste boşsa sıfırdır.

Tuples

Tuple'lar aynı zamanda içlerinde bir dizi başka yapıyı depolayan veri yapılarıdır. Listelerden farklı olarak, öğeleri bitişik bir bellek bloğunda saklarlar. Bu, indeks başına bir tuple öğesine erişmenin veya tuple boyutunu almanın hızlı bir işlem olduğu anlamına gelir. Dizinler sıfırdan başlar.

Elixir, tupleları tanımlamak için küme parantezleri kullanır. Listeler gibi, tuplelar da herhangi bir değeri tutabilir -

{:ok, "hello"}

Bir Demetin Uzunluğu

Bir demetin uzunluğunu elde etmek için, tuple_size aşağıdaki programdaki gibi işlev görür -

IO.puts(tuple_size({:ok, "hello"}))

Yukarıdaki program aşağıdaki sonucu üretir -

2

Bir Değer Eklemek

Başlığa bir değer eklemek için Tuple.append işlevini kullanın -

tuple = {:ok, "Hello"}
Tuple.append(tuple, :world)

Bu, yeni bir demet oluşturacak ve döndürecektir: {: ok, "Merhaba",: dünya}

Bir Değer Eklemek

Belirli bir konuma bir değer eklemek için, Tuple.insert_at function veya the put_elemişlevi. Aynısını anlamak için aşağıdaki örneği düşünün -

tuple = {:bar, :baz}
new_tuple_1 = Tuple.insert_at(tuple, 0, :foo)
new_tuple_2 = put_elem(tuple, 1, :foobar)

Dikkat edin put_elem ve insert_atyeni demetler döndürdü. Tuple değişkeninde depolanan orijinal demet, Elixir veri türleri değişmez olduğu için değiştirilmedi. Değişmez olduğu için Elixir kodunun akıl yürütmesi daha kolaydır, çünkü belirli bir kod veri yapınızı yerinde değiştiriyorsa endişelenmenize gerek kalmaz.

Tuples ve Listeler

Listeler ve demetler arasındaki fark nedir?

Listeler, bağlantılı listeler olarak bellekte depolanır, yani listedeki her öğenin değerini koruduğu ve listenin sonuna ulaşılana kadar aşağıdaki öğeyi gösterdiği anlamına gelir. Her bir değer çiftini çağırıyoruz ve bir eksiler hücresi gösteriyoruz. Bu, bir listenin uzunluğuna erişmenin doğrusal bir işlem olduğu anlamına gelir: Büyüklüğünü anlamak için tüm listeyi taramamız gerekir. Öğeleri önceden eklediğimiz sürece bir listeyi güncellemek hızlıdır.

Öte yandan, diziler bitişik olarak bellekte depolanır. Bu, tuple boyutunu almanın veya indeksle bir öğeye erişmenin hızlı olduğu anlamına gelir. Ancak, kayıtlara eleman eklemek veya güncellemek pahalıdır çünkü hafızadaki tüm demetin kopyalanmasını gerektirir.

Şimdiye kadar, herhangi bir ilişkilendirilebilir veri yapısını, yani belirli bir değeri (veya birden çok değeri) bir anahtarla ilişkilendirebilen veri yapılarını tartışmadık. Farklı diller bu özellikleri sözlükler, karmalar, ilişkilendirilebilir diziler vb. Gibi farklı adlarla adlandırır.

Elixir'de iki ana ilişkilendirilebilir veri yapımız vardır: anahtar kelime listeleri ve haritalar. Bu bölümde Anahtar Kelime listelerine odaklanacağız.

Pek çok işlevsel programlama dilinde, bir ilişkisel veri yapısının temsili olarak 2 maddelik demetlerin bir listesini kullanmak yaygındır. Elixir'de, bir tuple listemiz olduğunda ve tuple'ın ilk öğesi (yani anahtar) bir atom olduğunda, buna bir anahtar kelime listesi diyoruz. Aynısını anlamak için aşağıdaki örneği düşünün -

list = [{:a, 1}, {:b, 2}]

Elixir, bu tür listeleri tanımlamak için özel bir sözdizimini destekler. Kolonu her atomun sonuna yerleştirebiliriz ve tuple'lerden tamamen kurtulabiliriz. Örneğin,

list_1 = [{:a, 1}, {:b, 2}]
list_2 = [a: 1, b: 2]
IO.puts(list_1 == list_2)

Yukarıdaki program aşağıdaki sonucu üretecektir -

true

Bunların her ikisi de bir anahtar kelime listesini temsil eder. Anahtar kelime listeleri aynı zamanda liste olduğu için listelerde kullandığımız tüm işlemleri üzerlerinde kullanabiliriz.

Anahtar kelime listesindeki bir atomla ilişkili değeri almak için, atomu listenin adından sonra [] olarak iletin -

list = [a: 1, b: 2]
IO.puts(list[:a])

Yukarıdaki program aşağıdaki sonucu üretir -

1

Anahtar kelime listelerinin üç özel özelliği vardır -

  • Anahtarlar atom olmalıdır.
  • Anahtarlar, geliştirici tarafından belirtildiği gibi sıralanır.
  • Anahtarlar birden fazla verilebilir.

Elixir, anahtar kelime listelerini değiştirmek için Anahtar Kelime modülünü sağlar . Bununla birlikte, anahtar kelime listelerinin basit bir liste olduğunu ve bu nedenle listelerle aynı doğrusal performans özelliklerini sağladıklarını unutmayın. Liste ne kadar uzun olursa, anahtar bulmak, öğe sayısını saymak vb. O kadar uzun sürecektir. Bu nedenle Elixir'de anahtar kelime listeleri esas olarak seçenek olarak kullanılır. Çok sayıda öğe depolamanız veya tek anahtarlı ortaklara maksimum tek değer garantisi vermeniz gerekiyorsa, bunun yerine haritaları kullanmalısınız.

Bir anahtara erişim

Belirli bir anahtarla ilişkili değerlere erişmek için, Keyword.getişlevi. Verilen anahtarla ilişkili ilk değeri döndürür. Tüm değerleri almak için Keyword.get_values ​​fonksiyonunu kullanıyoruz. Örneğin -

kl = [a: 1, a: 2, b: 3] 
IO.puts(Keyword.get(kl, :a)) 
IO.puts(Keyword.get_values(kl))

Yukarıdaki program aşağıdaki sonucu üretecektir -

1
[1, 2]

Anahtar eklemek

Yeni bir değer eklemek için kullanın Keyword.put_new. Anahtar zaten varsa, değeri değişmeden kalır -

kl = [a: 1, a: 2, b: 3]
kl_new = Keyword.put_new(kl, :c, 5)
IO.puts(Keyword.get(kl_new, :c))

Yukarıdaki program çalıştırıldığında, ek anahtar, c ile yeni bir Anahtar Kelime listesi oluşturur ve aşağıdaki sonucu üretir -

5

Bir anahtarı silme

Bir anahtar için tüm girişleri silmek isterseniz, Keyword.delete; bir anahtar için yalnızca ilk girişi silmek için Keyword.delete_first.

kl = [a: 1, a: 2, b: 3, c: 0]
kl = Keyword.delete_first(kl, :b)
kl = Keyword.delete(kl, :a)

IO.puts(Keyword.get(kl, :a))
IO.puts(Keyword.get(kl, :b))
IO.puts(Keyword.get(kl, :c))

Bu ilkini silecek b Listede ve tüm alistede. Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretecektir -

0

Anahtar kelime listeleri, listelerde saklanan içeriği anahtarla ele almanın uygun bir yoludur, ancak bunun altında Elixir hala listede gezinmektedir. Bu liste için tümünün üzerinden geçmeyi gerektiren başka planlarınız varsa bu uygun olabilir, ancak verilere tek yaklaşımınız olarak anahtarları kullanmayı planlıyorsanız bu gereksiz bir ek yük olabilir.

Haritaların kurtarmaya geldiği yer burasıdır. Bir anahtar-değer deposuna ihtiyacınız olduğunda, haritalar Elixir'deki "git" veri yapısıdır.

Harita Oluşturmak

% {} Sözdizimi kullanılarak bir harita oluşturulur -

map = %{:a => 1, 2 => :b}

Anahtar kelime listelerine kıyasla, şimdiden iki fark görebiliriz -

  • Haritalar, herhangi bir değere anahtar olarak izin verir.
  • Haritaların anahtarları herhangi bir sıralamaya uymaz.

Bir anahtara erişim

Bir anahtarla ilişkili değere erişmek için Haritalar, Anahtar Kelime listeleriyle aynı sözdizimini kullanır -

map = %{:a => 1, 2 => :b}
IO.puts(map[:a])
IO.puts(map[2])

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretir -

1
b

Anahtar eklemek

Haritaya bir anahtar eklemek için, Dict.put_new haritayı, yeni anahtarı ve yeni değeri bağımsız değişken olarak alan işlev -

map = %{:a => 1, 2 => :b}
new_map = Dict.put_new(map, :new_val, "value") 
IO.puts(new_map[:new_val])

Bu, anahtar / değer çiftini ekler :new_val - "value"yeni bir haritada. Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretir -

"value"

Bir Değerin Güncellenmesi

Haritada zaten mevcut olan bir değeri güncellemek için aşağıdaki sözdizimini kullanabilirsiniz -

map = %{:a => 1, 2 => :b}
new_map = %{ map | a: 25}
IO.puts(new_map[:a])

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretir -

25

Desen Eşleştirme

Anahtar kelime listelerinin aksine, haritalar desen eşleştirmede çok kullanışlıdır. Bir modelde bir harita kullanıldığında, her zaman verilen değerin bir alt kümesiyle eşleşecektir -

%{:a => a} = %{:a => 1, 2 => :b}
IO.puts(a)

Yukarıdaki program aşağıdaki sonucu üretir -

1

Bu eşleşecek a ile 1. Ve dolayısıyla çıktıyı şu şekilde üretecektir:1.

Yukarıda gösterildiği gibi, modeldeki anahtarlar verilen haritada bulunduğu sürece bir harita eşleşir. Bu nedenle, boş bir harita tüm haritalarla eşleşir.

Değişkenler harita anahtarlarına erişilirken, eşleştirilirken ve eklenirken kullanılabilir -

n = 1
map = %{n => :one}
%{^n => :one} = %{1 => :one, 2 => :two, 3 => :three}

Harita modülü , haritaları işlemek için kullanışlı fonksiyonlara sahip Anahtar Kelime modülüne çok benzer bir API sağlar. Gibi işlevleri kullanabilirsiniz.Map.get, Map.delete, haritaları işlemek için.

Atom anahtarlı haritalar

Haritalar birkaç ilginç özelliğe sahiptir. Bir haritadaki tüm anahtarlar atom olduğunda, kolaylık sağlamak için anahtar kelime sözdizimini kullanabilirsiniz -

map = %{:a => 1, 2 => :b} 
IO.puts(map.a)

Haritaların bir başka ilginç özelliği de atom anahtarlarını güncellemek ve bunlara erişmek için kendi sözdizimini sağlamalarıdır -

map = %{:a => 1, 2 => :b}
IO.puts(map.a)

Yukarıdaki program aşağıdaki sonucu üretir -

1

Atom anahtarlarına bu şekilde erişmek için var olması gerektiğini veya programın çalışmayacağını unutmayın.

Elixir'de birkaç işlevi modüller halinde gruplandırıyoruz. Önceki bölümlerde String modülü, Bitwise modülü, Tuple modülü gibi farklı modülleri zaten kullandık.

Elixir'de kendi modüllerimizi oluşturmak için, defmodulemakro. Kullanıyoruzdef bu modüldeki işlevleri tanımlamak için makro -

defmodule Math do
   def sum(a, b) do
      a + b
   end
end

Aşağıdaki bölümlerde, örneklerimizin boyutu uzayacak ve hepsini kabukta yazmak zor olabilir. Elixir kodunu nasıl derleyeceğimizi ve ayrıca Elixir komut dosyalarını nasıl çalıştıracağımızı öğrenmemiz gerekiyor.

Derleme

Modülleri, derlenebilmeleri ve yeniden kullanılabilmeleri için dosyalara yazmak her zaman uygundur. Aşağıdaki içeriğe sahip math.ex adında bir dosyamız olduğunu varsayalım -

defmodule Math do
   def sum(a, b) do
      a + b
   end
end

Dosyaları şu komutu kullanarak derleyebiliriz -elixirc :

$ elixirc math.ex

Bu, adlı bir dosya oluşturacaktır. Elixir.Math.beamtanımlı modül için bayt kodunu içerir. Başlarsakiexyine, modül tanımımız mevcut olacaktır (iex'in bayt kodu dosyasının bulunduğu dizinde başlatılması koşuluyla). Örneğin,

IO.puts(Math.sum(1, 2))

Yukarıdaki program aşağıdaki sonucu üretecektir -

3

Komut Dosyası Modu

Elixir dosya uzantısına ek olarak .exElixir ayrıca şunları da destekler: .exskomut dosyası oluşturma dosyaları. Elixir her iki dosyayı da tamamen aynı şekilde ele alır, tek fark amaçtır..ex .exs dosyaları için kullanılırken dosyaların derlenmesi amaçlanmıştır scripting. Yürütüldüğünde, her iki uzantı da modüllerini derler ve belleğe yükler, ancak yalnızca.ex dosyalar bayt kodlarını diske .beam dosyaları biçiminde yazar.

Örneğin, Math.sum aynı dosyada .ex'leri şu şekilde kullanabiliriz -

Math.exs

defmodule Math do
   def sum(a, b) do
      a + b
   end
end
IO.puts(Math.sum(1, 2))

Elixir komutunu kullanarak çalıştırabiliriz -

$ elixir math.exs

Yukarıdaki program aşağıdaki sonucu üretecektir -

3

Dosya bellekte derlenecek ve sonuç olarak “3” yazdırılarak yürütülecektir. Bayt kodu dosyası oluşturulmayacaktır.

Modül Yerleştirme

Modüller Elixir'e yerleştirilebilir. Dilin bu özelliği, kodumuzu daha iyi bir şekilde düzenlememize yardımcı olur. İç içe modüller oluşturmak için aşağıdaki sözdizimini kullanıyoruz -

defmodule Foo do
   #Foo module code here
   defmodule Bar do
      #Bar module code here
   end
end

Yukarıda verilen örnek iki modülü tanımlayacaktır: Foo ve Foo.Bar. İkincisine şu şekilde erişilebilir:Bar içeride Fooaynı sözlü kapsamda oldukları sürece. Daha sonraBar modül Foo modül tanımının dışına taşınırsa, tam adıyla (Foo.Bar) başvurulmalıdır veya alias bölümünde tartışılan alias yönergesi kullanılarak bir takma ad ayarlanmalıdır.

Note- Elixir'de, dil tüm modül adlarını atomlara çevirdiğinden, Foo.Bar modülünü tanımlamak için Foo modülünü tanımlamaya gerek yoktur. Zincirde herhangi bir modül tanımlamadan arbitrarilynested modülleri tanımlayabilirsiniz. Örneğin, tanımlayabilirsinizFoo.Bar.Baz tanımlamadan Foo veya Foo.Bar.

Yazılımın yeniden kullanımını kolaylaştırmak için Elixir üç yönerge sağlar - alias, require ve import. Ayrıca, aşağıda özetlenen kullanım adı verilen bir makro sağlar -

# Alias the module so it can be called as Bar instead of Foo.Bar
alias Foo.Bar, as: Bar

# Ensure the module is compiled and available (usually for macros)
require Foo

# Import functions from Foo so they can be called without the `Foo.` prefix
import Foo

# Invokes the custom code defined in Foo as an extension point
use Foo

Şimdi her bir yönergeyi ayrıntılı olarak anlayalım.

takma ad

Alias ​​yönergesi, herhangi bir modül adı için takma adlar belirlemenize izin verir. Örneğin, bir takma ad vermek istiyorsanız'Str' String modülüne yazabilirsiniz -

alias String, as: Str
IO.puts(Str.length("Hello"))

Yukarıdaki program aşağıdaki sonucu üretir -

5

Bir takma ad verilir String modül olarak Str. Şimdi, Str değişmezini kullanarak herhangi bir işlevi çağırdığımızda, aslındaStringmodül. Bu, çok uzun modül adları kullandığımızda ve mevcut kapsamda daha kısa olanları değiştirmek istediğimizde çok yararlıdır.

NOTE - Takma adlar MUST büyük harfle başlayın.

Takma adlar yalnızca içinde geçerlidir lexical scope bunlar çağrılır. Örneğin, bir dosyada 2 modülünüz varsa ve modüllerden birinde bir takma ad yaparsanız, bu takma ada ikinci modülde erişilemez.

Dahili modüle erişmek için String veya Tuple gibi yerleşik bir modülün adını başka bir modüle takma ad olarak verirseniz, bunu başına eklemeniz gerekir. "Elixir.". Örneğin,

alias List, as: String
#Now when we use String we are actually using List.
#To use the string module: 
IO.puts(Elixir.String.length("Hello"))

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretir -

5

gerek

Elixir, meta programlama için bir mekanizma olarak makrolar sağlar (kod üreten kod yazma).

Makrolar, derleme zamanında çalıştırılan ve genişletilen kod parçalarıdır. Bu, bir makroyu kullanmak için, modülünün ve uygulamasının derleme sırasında kullanılabilir olduğunu garanti etmemiz gerektiği anlamına gelir. Bu,require direktif.

Integer.is_odd(3)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretecektir -

** (CompileError) iex:1: you must require Integer before invoking the macro Integer.is_odd/1

İksirde, Integer.is_odd olarak tanımlanır macro. Bu makro bir koruma olarak kullanılabilir. Bu, çağırmak içinInteger.is_oddTamsayı modülüne ihtiyacımız olacak.

Kullan require Integer programı aşağıda gösterildiği gibi çalıştırın.

require Integer
Integer.is_odd(3)

Bu sefer program çalışacak ve çıktıyı şu şekilde üretecektir: true.

Genel olarak, o modülde bulunan makroları kullanmak istememiz dışında, kullanımdan önce bir modül gerekli değildir. Yüklenmemiş bir makroyu çağırma denemesi bir hataya neden olur. Takma ad yönergesi gibi, require'in de sözcüksel kapsamda olduğunu unutmayın . Sonraki bölümde makrolar hakkında daha fazla konuşacağız.

ithalat

Kullanıyoruz importtam yetkili adı kullanmadan diğer modüllerden işlevlere veya makrolara kolayca erişme yönergesi. Örneğin, kullanmak istiyorsakduplicate List modülünden birkaç kez işlev görürseniz, onu kolayca içe aktarabiliriz.

import List, only: [duplicate: 2]

Bu durumda, List'den sadece yinelenen işlevi (argüman listesi uzunluğu 2 ile) içe aktarıyoruz. olmasına rağmen:only isteğe bağlıdır, belirli bir modülün tüm işlevlerinin ad alanı içine aktarılmasını önlemek için kullanılması önerilir. :except işlevler listesi dışında bir modüldeki her şeyi içe aktarmak için bir seçenek olarak da verilebilir.

import direktif ayrıca destekler :macros ve :functions verilmek üzere :only. Örneğin, tüm makroları içe aktarmak için bir kullanıcı yazabilir -

import Integer, only: :macros

İçe aktarmanın da Lexically scopedtıpkı zorunlu ve takma ad yönergeleri gibi. Ayrıca şunu unutmayın'import'ing a module also 'require's it.

kullanım

Yönerge olmasa da, use ile yakından ilişkili bir makrodur requirebu, mevcut bağlamda bir modülü kullanmanıza izin verir. Kullanım makrosu, geliştiriciler tarafından, genellikle modüller olmak üzere, harici işlevselliği mevcut sözcük kapsamına getirmek için sıklıkla kullanılır. Kullanım direktifini bir örnekle anlayalım -

defmodule Example do 
   use Feature, option: :value 
end

Kullanım, yukarıdakileri -

defmodule Example do
   require Feature
   Feature.__using__(option: :value)
end

use Module önce modülü gerektirir ve ardından __using__Modül üzerindeki makro. Elixir'in mükemmel meta programlama yetenekleri vardır ve derleme zamanında kod üretmek için makroları vardır. _ _Using__ makrosu yukarıdaki örnekte çağrılır ve kod yerel bağlamımıza enjekte edilir. Yerel bağlam, derleme sırasında kullanım makrosunun çağrıldığı yerdir .

Bir işlev, belirli bir görevi gerçekleştirmek için birlikte düzenlenen bir dizi ifadedir. Programlamadaki fonksiyonlar çoğunlukla Math'daki fonksiyon gibi çalışır. İşlevlere bir miktar girdi verirsiniz, sağlanan girdiye göre çıktı üretirler.

Elixir'de 2 tür işlev vardır -

Anonim işlev

Kullanılarak tanımlanan fonksiyonlar fn..end constructanonim işlevlerdir. Bu işlevler bazen lambdas olarak da adlandırılır. Değişken isimlerine atanarak kullanılırlar.

İsimli işlev

Kullanılarak tanımlanan fonksiyonlar def keywordadlandırılmış işlevlerdir. Bunlar Elixir'de sağlanan yerel işlevlerdir.

Anonim İşlevler

Adından da anlaşılacağı gibi, anonim bir işlevin adı yoktur. Bunlar sıklıkla diğer işlevlere aktarılır. Elixir'de anonim bir işlevi tanımlamak için,fn ve endanahtar kelimeler. Bunların içinde, herhangi bir sayıda parametre ve fonksiyon gövdesini,->. Örneğin,

sum = fn (a, b) -> a + b end
IO.puts(sum.(1, 5))

Yukarıdaki program çalıştırıldığında, çalıştırıldığında aşağıdaki sonucu üretir -

6

Bu işlevlerin adlandırılmış işlevler gibi çağrılmadığını unutmayın. Bir '.işlev adı ve bağımsız değişkenleri arasında.

Yakalama Operatörünü Kullanma

Bu işlevleri, yakalama operatörünü kullanarak da tanımlayabiliriz. Bu, işlevler oluşturmak için daha kolay bir yöntemdir. Şimdi yakalama operatörünü kullanarak yukarıdaki toplam işlevini tanımlayacağız,

sum = &(&1 + &2) 
IO.puts(sum.(1, 2))

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretir -

3

Kısa versiyonda, parametrelerimiz adlandırılmaz, ancak bizim için & 1, & 2, & 3, vb. Olarak kullanılabilir.

Desen Eşleştirme İşlevleri

Örüntü eşleştirme yalnızca değişkenler ve veri yapılarıyla sınırlı değildir. İşlevlerimizi polimorfik yapmak için örüntü eşleştirmeyi kullanabiliriz. Örneğin, 1 veya 2 giriş (bir tuple içinde) alabilen ve bunları konsola yazdırabilen bir işlev ilan edeceğiz,

handle_result = fn
   {var1} -> IO.puts("#{var1} found in a tuple!")
   {var_2, var_3} -> IO.puts("#{var_2} and #{var_3} found!")
end
handle_result.({"Hey people"})
handle_result.({"Hello", "World"})

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hey people found in a tuple!
Hello and World found!

Adlandırılmış İşlevler

Fonksiyonları isimlerle tanımlayabiliriz, böylece onlara daha sonra kolayca başvurabiliriz. Adlandırılmış işlevler, def anahtar sözcüğü kullanılarak bir modül içinde tanımlanır. Adlandırılmış işlevler her zaman bir modülde tanımlanır. Adlandırılmış işlevleri çağırmak için, modül adlarını kullanarak onlara başvurmamız gerekir.

Aşağıda adlandırılmış işlevlerin sözdizimi verilmiştir -

def function_name(argument_1, argument_2) do
   #code to be executed when function is called
end

Şimdi Math modülünde adlandırılmış fonksiyon toplamımızı tanımlayalım.

defmodule Math do
   def sum(a, b) do
      a + b
   end
end

IO.puts(Math.sum(5, 6))

Yukarıdaki programı çalıştırırken, aşağıdaki sonucu verir -

11

1 satırlık işlevler için, bu işlevleri tanımlamak için bir kısaltma notasyonu vardır. do:. Örneğin -

defmodule Math do
   def sum(a, b), do: a + b
end
IO.puts(Math.sum(5, 6))

Yukarıdaki programı çalıştırırken, aşağıdaki sonucu verir -

11

Özel İşlevler

Elixir bize, tanımlandıkları modülün içinden erişilebilen özel fonksiyonları tanımlama olanağı sağlar. Özel bir işlevi tanımlamak için kullanındefp onun yerine def. Örneğin,

defmodule Greeter do
   def hello(name), do: phrase <> name
   defp phrase, do: "Hello "
end

Greeter.hello("world")

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hello world

Ancak, ifade işlevini açıkça çağırmaya çalışırsak, Greeter.phrase() işlevi, bir hata oluşturacaktır.

Varsayılan argümanlar

Bir bağımsız değişken için varsayılan bir değer istiyorsak, argument \\ value sözdizimi -

defmodule Greeter do
   def hello(name, country \\ "en") do
      phrase(country) <> name
   end

   defp phrase("en"), do: "Hello, "
   defp phrase("es"), do: "Hola, "
end

Greeter.hello("Ayush", "en")
Greeter.hello("Ayush")
Greeter.hello("Ayush", "es")

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hello, Ayush
Hello, Ayush
Hola, Ayush

Özyineleme, bir sorunun çözümünün, aynı sorunun daha küçük örneklerinin çözümlerine bağlı olduğu bir yöntemdir. Çoğu bilgisayar programlama dili, bir işlevin kendisini program metni içinde çağırmasına izin vererek özyinelemeyi destekler.

İdeal olarak özyinelemeli fonksiyonların bir bitiş koşulu vardır. Temel durum olarak da bilinen bu sonlandırma koşulu, işleve yeniden girmeyi ve yığına işlev çağrıları eklemeyi durdurur. Özyinelemeli işlev çağrısının durduğu yer burasıdır. Özyinelemeli işlevi daha iyi anlamak için aşağıdaki örneği ele alalım.

defmodule Math do
   def fact(res, num) do
   if num === 1 do
      res
   else
      new_res = res * num
      fact(new_res, num-1)
      end
   end
end

IO.puts(Math.fact(1,5))

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu üretir -

120

Yani yukarıdaki işlevde, Math.fact, bir sayının faktöriyelini hesaplıyoruz. İşlevi kendi içinde çağırdığımıza dikkat edin. Şimdi bunun nasıl çalıştığını anlayalım.

Buna 1 ve faktöriyelini hesaplamak istediğimiz sayıyı verdik. İşlev, sayının 1 olup olmadığını kontrol eder ve 1 ise res döndürür.(Ending condition). Değilse, bir new_res değişkeni yaratır ve ona önceki res * mevcut num değerini atar. İşlev çağrımız fact (new_res, num-1) tarafından döndürülen değeri döndürür . Bu, 1 olarak sayı alana kadar tekrar eder. Bu gerçekleştiğinde, sonucu alırız.

Listenin her bir öğesini tek tek yazdırarak başka bir örneği ele alalım. Bunu yapmak için kullanacağızhd ve tl listelerin işlevleri ve işlevlerde kalıp eşleştirme -

a = ["Hey", 100, 452, :true, "People"]
defmodule ListPrint do
   def print([]) do
   end
   def print([head | tail]) do 
      IO.puts(head)
      print(tail)
   end
end

ListPrint.print(a)

İlk yazdırma işlevi boş bir listemiz olduğunda çağrılır(ending condition). Değilse, listeyi 2'ye bölen ve listenin ilk öğesini başa, kalanını kuyruğa atayan ikinci yazdırma işlevi çağrılacaktır. Daha sonra kafa yazdırılır ve listenin geri kalanıyla, yani kuyrukla birlikte tekrar yazdırma işlevini çağırırız. Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hey
100
452
true
People

Değişmezlik nedeniyle, Elixir'deki döngüler (herhangi bir işlevsel programlama dilinde olduğu gibi) zorunlu dillerden farklı şekilde yazılmıştır. Örneğin, C gibi zorunlu bir dilde, yazacaksın -

for(i = 0; i < 10; i++) {
   printf("%d", array[i]);
}

Yukarıda verilen örnekte, hem diziyi hem de değişkeni değiştiriyoruz i. Elixir'de mutasyon mümkün değildir. Bunun yerine, işlevsel diller özyinelemeye dayanır: özyinelemeli eylemin devam etmesini durduran bir duruma ulaşılana kadar bir işlev özyinelemeli olarak çağrılır. Bu süreçte hiçbir veri mutasyona uğramaz.

Şimdi merhaba yazan özyinelemeyi kullanarak basit bir döngü yazalım n zamanlar.

defmodule Loop do
   def print_multiple_times(msg, n) when n <= 1 do
      IO.puts msg
   end

   def print_multiple_times(msg, n) do
      IO.puts msg
      print_multiple_times(msg, n - 1)
   end
end

Loop.print_multiple_times("Hello", 10)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello

Bir döngüyü başarılı bir şekilde gerçekleştirmek için fonksiyonun kalıp eşleştirme tekniklerini ve özyinelemeyi kullandık. Özyinelemeli tanımları anlamak zordur ancak döngüleri özyinelemeye dönüştürmek kolaydır.

İksir bize Enum module. Bu modül, çoğu yinelemeli döngü çağrıları için kullanılır, çünkü bunları kullanmak, aynı için özyinelemeli tanımları bulmaya çalışmaktan çok daha kolaydır. Bunları bir sonraki bölümde tartışacağız. Kendi özyinelemeli tanımlarınız yalnızca o modülü kullanarak bir çözüm bulamadığınız zaman kullanılmalıdır. Bu işlevler, kuyruk çağrısı optimize edilmiş ve oldukça hızlıdır.

Numaralandırılabilir, numaralandırılabilen bir nesnedir. "Numaralandırılmış", bir kümenin / koleksiyonun / kategorinin üyelerini tek tek saymak anlamına gelir (genellikle sırayla, genellikle adıyla).

Elixir, numaralandırılabilir kavramları ve bunlarla çalışmak için Enum modülünü sağlar. Enum modülündeki işlevler, adından da anlaşılacağı gibi, veri yapılarındaki değerleri numaralandırmakla sınırlıdır. Numaralandırılabilir bir veri yapısı örneği, bir liste, kayıt, harita, vs.'dir. Enum modülü, numaralandırmalarla başa çıkmak için bize 100'den biraz fazla işlev sağlar. Bu bölümde birkaç önemli işlevi tartışacağız.

Bu işlevlerin tümü, birinci öğe olarak bir numaralandırılabilir ve ikinci öğe olarak bir işlevi alır ve bunlar üzerinde çalışır. Fonksiyonlar aşağıda açıklanmıştır.

herşey?

Kullandığımız zaman all? işlev, koleksiyonun tamamı doğru olarak değerlendirilmelidir, aksi takdirde yanlış döndürülür. Örneğin, listedeki tüm elemanların tek sayı olup olmadığını kontrol etmek için, o zaman.

res = Enum.all?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end) 
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

false

Bunun nedeni, bu listenin tüm öğelerinin tuhaf olmamasıdır.

hiç?

Adından da anlaşılacağı gibi, koleksiyonun herhangi bir öğesi doğru olarak değerlendirilirse bu işlev true değerini döndürür. Örneğin -

res = Enum.any?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end)
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

true

yığın

Bu işlev, koleksiyonumuzu ikinci argüman olarak sağlanan boyutta küçük parçalara böler. Örneğin -

res = Enum.chunk([1, 2, 3, 4, 5, 6], 2)
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

[[1, 2], [3, 4], [5, 6]]

her biri

Yeni bir değer üretmeden bir koleksiyon üzerinde yineleme yapmak gerekebilir, bu durumda each işlev -

Enum.each(["Hello", "Every", "one"], fn(s) -> IO.puts(s) end)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hello
Every
one

harita

İşlevimizi her öğeye uygulamak ve yeni bir koleksiyon oluşturmak için harita işlevini kullanıyoruz. Oldukça etkileyici ve kısa olduğu için fonksiyonel programlamada en kullanışlı yapılardan biridir. Bunu anlamak için bir örnek ele alalım. Bir listede saklanan değerleri ikiye katlayacağız ve yeni bir listede saklayacağızres -

res = Enum.map([2, 5, 3, 6], fn(a) -> a*2 end)
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

[4, 10, 6, 12]

azaltmak

reduceişlevi, numaralandırılmamızı tek bir değere indirmemize yardımcı olur. Bunu yapmak için, fonksiyonumuza geçirilecek isteğe bağlı bir akümülatör (bu örnekte 5) sağlıyoruz; akümülatör sağlanmadıysa, ilk değer kullanılır -

res = Enum.reduce([1, 2, 3, 4], 5, fn(x, accum) -> x + accum end)
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

15

Akümülatör, aktarılan başlangıç ​​değeridir. fn. İkinci aramadan itibaren, önceki aramadan dönen değer birikmiş olarak aktarılır. Ayrıca akümülatör olmadan azaltmayı da kullanabiliriz -

res = Enum.reduce([1, 2, 3, 4], fn(x, accum) -> x + accum end)
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

10

uniq

Uniq işlevi, koleksiyonumuzdan kopyaları kaldırır ve yalnızca koleksiyondaki öğe kümesini döndürür. Örneğin -

res = Enum.uniq([1, 2, 2, 3, 3, 3, 4, 4, 4, 4])
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

[1, 2, 3, 4]

Hevesli Değerlendirme

Enum modülündeki tüm işlevler isteklidir. Çoğu işlev bir numaralandırılabilir ve bir listeyi geri döndürür. Bu, Enum ile birden çok işlem gerçekleştirirken, sonuca ulaşana kadar her işlemin bir ara liste oluşturacağı anlamına gelir. Bunu anlamak için aşağıdaki örneği ele alalım -

odd? = &(odd? = &(rem(&1, 2) != 0) 
res = 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum 
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

7500000000

Yukarıdaki örnek bir işlem hattına sahiptir. Bir aralıkla başlıyoruz ve ardından aralıktaki her bir öğeyi 3 ile çarpıyoruz. Bu ilk işlem şimdi 100_000 öğeli bir liste oluşturacak ve döndürecektir. Sonra listedeki tüm garip öğeleri saklarız, şimdi 50_000 öğe içeren yeni bir liste oluştururuz ve sonra tüm girişleri toplarız.

|> Yukarıdaki kod parçacığında kullanılan sembol, pipe operator: Çıktıyı sol tarafındaki ifadeden alır ve ilk argüman olarak sağ tarafındaki işlev çağrısına iletir. Unix'e benzer | Şebeke. Amacı, bir dizi işlev tarafından dönüştürülen veri akışını vurgulamaktır.

Olmadan pipe operatör, kod karmaşık görünüyor -

Enum.sum(Enum.filter(Enum.map(1..100_000, &(&1 * 3)), odd?))

Diğer birçok fonksiyonumuz var, ancak burada sadece birkaç önemli fonksiyon açıklanmıştır.

Pek çok işlev bir numaralandırılabilir ve bir listgeri. Bu, Enum ile birden fazla işlem gerçekleştirirken, sonuca ulaşana kadar her işlemin bir ara liste oluşturacağı anlamına gelir.

Akışlar, numaralandırma tarafından yapılan istekli işlemlerin aksine geç işlemleri destekler. Kısacası,streams are lazy, composable enumerables. Bunun anlamı, Akışların kesinlikle gerekli olmadıkça bir işlem gerçekleştirmemesidir. Bunu anlamak için bir örnek ele alalım -

odd? = &(rem(&1, 2) != 0)
res = 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum
IO.puts(res)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

7500000000

Yukarıda verilen örnekte, 1..100_000 |> Stream.map(&(&1 * 3))1..100_000 aralığında harita hesaplamasını temsil eden bir veri türü, gerçek bir akış döndürür. Henüz bu temsili değerlendirmedi. Akışlar, ara listeler oluşturmak yerine, yalnızca temel alınan akışı Enum modülüne ilettiğimizde çağrılan bir dizi hesaplama oluşturur. Akışlar, büyük, muhtemelen sonsuz koleksiyonlarla çalışırken kullanışlıdır.

Akışlar ve numaralandırmaların birçok ortak işlevi vardır. Akışlar, esas olarak, girdi numaralandırılabilirleri üzerinde hesaplamalar gerçekleştirdikten sonra, Listeleri dönüş değerleri olarak oluşturan Enum modülü tarafından sağlanan aynı işlevleri sağlar. Bunlardan bazıları aşağıdaki tabloda listelenmiştir -

Sr.No. İşlev ve Açıklaması
1

chunk(enum, n, step, leftover \\ nil)

Numaralandırılabiliri, her biri n öğe içeren parçalar halinde aktarır, burada her yeni öbek, adım öğelerini numaralandırılabilir hale getirir.

2

concat(enumerables)

Numaralandırılabilir her bir numaralandırılabilir bir akış oluşturur.

3

each(enum, fun)

Her öğe için verilen işlevi çalıştırır.

4

filter(enum, fun)

Numaralandırmada verilen işleve göre öğeleri filtreleyen bir akış oluşturur.

5

map(enum, fun)

Verilen işlevi numaralandırmaya uygulayacak bir akış oluşturur.

6

drop(enum, n)

Sıralanabilirden sonraki n öğeyi tembelce bırakır.

Yapılar, derleme zamanı kontrolleri ve varsayılan değerler sağlayan, haritaların üzerine inşa edilmiş uzantılardır.

Yapıları Tanımlama

Bir yapıyı tanımlamak için defstruct yapısı kullanılır -

defmodule User do
   defstruct name: "John", age: 27
end

Defstruct ile kullanılan anahtar kelime listesi, yapının varsayılan değerleriyle birlikte hangi alanlara sahip olacağını tanımlar. Yapılar, tanımlandıkları modülün adını alırlar. Yukarıdaki örnekte, Kullanıcı adında bir yapı tanımladık. Artık harita oluşturmak için kullanılana benzer bir sözdizimi kullanarak Kullanıcı yapıları oluşturabiliriz -

new_john = %User{})
ayush = %User{name: "Ayush", age: 20}
megan = %User{name: "Megan"})

Yukarıdaki kod, değerleri olan üç farklı yapı oluşturacaktır -

%User{age: 27, name: "John"}
%User{age: 20, name: "Ayush"}
%User{age: 27, name: "Megan"}

Yapılar, yalnızca defstruct aracılığıyla tanımlanan alanların (ve hepsinin) bir yapı içinde var olacağına dair derleme zamanı garantileri sağlar. Dolayısıyla modülde yapıyı oluşturduktan sonra kendi alanlarınızı tanımlayamazsınız.

Yapılara Erişim ve Güncelleme

Haritaları tartıştığımızda, bir haritanın alanlarına nasıl erişebileceğimizi ve bunları nasıl güncelleyebileceğimizi gösterdik. Aynı teknikler (ve aynı sözdizimi) yapılar için de geçerlidir. Örneğin, önceki örnekte oluşturduğumuz kullanıcıyı güncellemek istiyorsak, o zaman -

defmodule User do
   defstruct name: "John", age: 27
end
john = %User{}
#john right now is: %User{age: 27, name: "John"}

#To access name and age of John, 
IO.puts(john.name)
IO.puts(john.age)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

John
27

Bir yapıdaki bir değeri güncellemek için, harita bölümünde kullandığımız prosedürün aynısını tekrar kullanacağız,

meg = %{john | name: "Meg"}

Yapılar, hem belirli anahtarların değeri üzerinde eşleştirme hem de eşleşen değerin eşleşen değerle aynı türde bir yapı olmasını sağlamak için desen eşleştirmede de kullanılabilir.

Protokoller, Elixir'de polimorfizme ulaşmak için bir mekanizmadır. Bir protokol üzerinde gönderim, protokolü uyguladığı sürece herhangi bir veri türü için kullanılabilir.

Protokol kullanımının bir örneğini ele alalım. Adlı bir işlev kullandıkto_stringönceki bölümlerde diğer türlerden dizgi türüne dönüştürmek için. Bu aslında bir protokoldür. Verilen girdiye göre hata üretmeden hareket eder. Bu, örüntü eşleştirme işlevlerini tartışıyormuşuz gibi görünebilir, ancak ilerledikçe durum farklı çıkıyor.

Protokol mekanizmasını daha iyi anlamak için aşağıdaki örneği düşünün.

Verilen girişin boş olup olmadığını gösterecek bir protokol oluşturalım. Bu protokolü arayacağızblank?.

Bir Protokol Tanımlamak

Elixir'de bir protokolü şu şekilde tanımlayabiliriz -

defprotocol Blank do
   def blank?(data)
end

Gördüğünüz gibi, işlev için bir gövde tanımlamamıza gerek yok. Diğer programlama dillerindeki arayüzlere aşina iseniz, bir Protokolü esasen aynı şey olarak düşünebilirsiniz.

Dolayısıyla bu Protokol, onu uygulayan herhangi bir şeyin bir empty?işlev, ancak işlevin nasıl tepki vereceği uygulayıcıya bağlıdır. Tanımlanan protokol ile birkaç uygulamanın nasıl ekleneceğini anlayalım.

Bir Protokolün Uygulanması

Bir protokol tanımladığımız için, şimdi ona alabileceği farklı girdileri nasıl kullanacağını söylememiz gerekiyor. Daha önce aldığımız örneği temel alalım. Listeler, haritalar ve dizeler için boş protokolü uygulayacağız. Bu, geçtiğimiz şeyin boş olup olmadığını gösterecektir.

#Defining the protocol
defprotocol Blank do
   def blank?(data)
end

#Implementing the protocol for lists
defimpl Blank, for: List do
   def blank?([]), do: true
   def blank?(_), do: false
end

#Implementing the protocol for strings
defimpl Blank, for: BitString do
   def blank?(""), do: true
   def blank?(_), do: false
end

#Implementing the protocol for maps
defimpl Blank, for: Map do
   def blank?(map), do: map_size(map) == 0
end

IO.puts(Blank.blank? [])
IO.puts(Blank.blank? [:true, "Hello"])
IO.puts(Blank.blank? "")
IO.puts(Blank.blank? "Hi")

Protokolünüzü istediğiniz kadar çok veya az tür için uygulayabilirsiniz, Protokolünüzün kullanımı için mantıklı olan her ne ise. Bu, protokollerin oldukça basit bir kullanım durumuydu. Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

true
false
true
false

Note - Bunu, protokolü tanımladığınız türler dışında kullanırsanız, bir hata oluşturur.

Dosya IO, dilin dosya sistemindeki dosyalarla etkileşime girmesine izin verdiği için herhangi bir programlama dilinin ayrılmaz bir parçasıdır. Bu bölümde, iki modülü tartışacağız - Yol ve Dosya.

Yol Modülü

pathmodül, dosya sistemi işlemleri için yardımcı bir modül olarak düşünülebilecek çok küçük bir modüldür. Dosya modülündeki işlevlerin çoğu, yolları bağımsız değişken olarak bekler. En yaygın olarak, bu yollar normal ikili dosyalar olacaktır. Yol modülü, bu tür yollarla çalışmak için kolaylıklar sağlar. Path modülü, farklı işletim sistemlerini şeffaf bir şekilde ele aldığından, yalnızca ikili dosyaları değiştirmek yerine Path modülündeki işlevleri kullanmak tercih edilir. Elixir'in dosya işlemleri gerçekleştirirken Windows'ta eğik çizgileri (/) otomatik olarak ters eğik çizgilere (\) dönüştüğü gözlemlenecektir.

Yol modülünü daha iyi anlamak için aşağıdaki örneği ele alalım -

IO.puts(Path.join("foo", "bar"))

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

foo/bar

Yol modülünün sağladığı birçok yöntem vardır. Farklı yöntemlere buradan göz atabilirsiniz . Bu yöntemler, çok sayıda dosya işleme işlemi gerçekleştiriyorsanız sıklıkla kullanılır.

Dosya Modülü

Dosya modülü, dosyaları IO cihazları olarak açmamıza izin veren işlevler içerir. Varsayılan olarak dosyalar ikili modda açılır ve geliştiricilerin belirliIO.binread ve IO.binwriteIO modülünden fonksiyonlar. Adlı bir dosya oluşturalımnewfile ve ona bazı veriler yazın.

{:ok, file} = File.read("newfile", [:write]) 
# Pattern matching to store returned stream
IO.binwrite(file, "This will be written to the file")

Az önce yazdığımız dosyayı açmaya giderseniz, içerik şu şekilde görüntülenecektir -

This will be written to the file

Şimdi dosya modülünün nasıl kullanılacağını anlayalım.

Bir dosyayı açmak

Bir dosyayı açmak için aşağıdaki 2 işlevden herhangi birini kullanabiliriz -

{:ok, file} = File.open("newfile")
file = File.open!("newfile")

Şimdi arasındaki farkı anlayalım File.open fonksiyon ve File.open!() işlevi.

  • File.openişlev her zaman bir demet döndürür. Dosya başarıyla açılırsa, dizideki ilk değeri şu şekilde döndürür::okve ikinci değer io_device türünün değişmez değeridir. Bir hataya neden olursa, ilk değeri olan bir demet döndürür::error ve sebep olarak ikinci değer.

  • File.open!() işlev ise bir io_devicedosya başarıyla açılırsa, başka bir hata ortaya çıkarır. NOT: Bu, tartışacağımız tüm dosya modülü işlevlerinde izlenen kalıptır.

Ayrıca bu dosyayı açmak istediğimiz modları da belirtebiliriz. Bir dosyayı salt okunur olarak ve utf-8 kodlama modunda açmak için aşağıdaki kodu kullanıyoruz -

file = File.open!("newfile", [:read, :utf8])

Dosyaya Yazmak

Dosyalara yazmanın iki yolu var. Dosya modülünden yazma işlevini kullanan ilkini görelim.

File.write("newfile", "Hello")

Ancak aynı dosyaya birden çok yazma işlemi yapıyorsanız bu kullanılmamalıdır. Bu işlev her çalıştırıldığında, bir dosya tanımlayıcısı açılır ve dosyaya yazmak için yeni bir işlem başlatılır. Bir döngüde birden fazla yazma yapıyorsanız, dosyayı şu yolla açın:File.openve IO modülündeki yöntemleri kullanarak ona yazın. Aynısını anlamak için bir örnek ele alalım -

#Open the file in read, write and utf8 modes. 
file = File.open!("newfile_2", [:read, :utf8, :write])

#Write to this "io_device" using standard IO functions
IO.puts(file, "Random text")

Gibi diğer IO modülü yöntemlerini kullanabilirsiniz IO.write ve IO.binwrite io_device olarak açılan dosyalara yazmak için.

Dosyadan Okuma

Dosyalardan okumanın iki yolu var. Dosya modülünden okuma işlevini kullanan ilkini görelim.

IO.puts(File.read("newfile"))

Bu kodu çalıştırırken, ilk elemanı olan bir demet almalısınız. :ok ve ikincisi yeni dosyanın içeriği olarak

Ayrıca kullanabiliriz File.read! sadece bize döndürülen dosyaların içeriğini alma işlevi.

Açık Bir Dosyayı Kapatma

File.open işlevini kullanarak bir dosyayı açtığınızda, onu kullanmayı bitirdikten sonra, onu kullanarak kapatmalısınız. File.close işlev -

File.close(file)

Elixir'de tüm kod süreçler içinde çalışır. Süreçler birbirlerinden izole edilir, eşzamanlı olarak çalışır ve mesaj geçişi yoluyla iletişim kurar. Elixir'in süreçleri işletim sistemi süreçleri ile karıştırılmamalıdır. Elixir'deki işlemler, bellek ve CPU açısından son derece hafiftir (diğer birçok programlama dilindeki iş parçacıklarının aksine). Bu nedenle, aynı anda çalışan onlarca hatta yüzbinlerce sürecin olması alışılmadık bir durum değildir.

Bu bölümde, farklı süreçler arasında mesaj gönderip almanın yanı sıra yeni süreçler üretmenin temel yapılarını öğreneceğiz.

Spawn Fonksiyonu

Yeni bir süreç oluşturmanın en kolay yolu, spawnişlevi. spawnyeni işlemde çalıştırılacak bir işlevi kabul eder. Örneğin -

pid = spawn(fn -> 2 * 2 end)
Process.alive?(pid)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

false

Spawn işlevinin dönüş değeri bir PID'dir. Bu, işlem için benzersiz bir tanımlayıcıdır ve bu nedenle, kodu PID'nizin üzerinde çalıştırırsanız, farklı olacaktır. Bu örnekte görebileceğiniz gibi, canlı olup olmadığını kontrol ettiğimizde süreç ölmüştür. Bunun nedeni, işlemin verilen işlevi çalıştırmayı bitirir bitirmez çıkmasıdır.

Daha önce de belirtildiği gibi, tüm Elixir kodları süreçler içinde çalışır. Kendi kendine işlevi çalıştırırsanız, mevcut oturumunuz için PID'yi göreceksiniz -

pid = self
 
Process.alive?(pid)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

true

İleti geçişi

İle bir sürece mesaj gönderebiliriz send ve onları al receive. Mevcut sürece bir mesaj iletelim ve aynı şekilde alalım.

send(self(), {:hello, "Hi people"})

receive do
   {:hello, msg} -> IO.puts(msg)
   {:another_case, msg} -> IO.puts("This one won't match!")
end

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hi people

Gönderme fonksiyonunu kullanarak mevcut sürece bir mesaj gönderdik ve bunu kendi PID'sine geçirdik. Ardından gelen mesajıreceive işlevi.

Bir işleme bir mesaj gönderildiğinde, mesaj process mailbox. Alma bloğu, verilen modellerden herhangi biriyle eşleşen bir mesajı arayan mevcut işlem posta kutusundan geçer. Alma bloğu, korumaları ve kasa gibi birçok maddeyi destekler.

Posta kutusunda herhangi bir örüntüyle eşleşen mesaj yoksa, mevcut işlem eşleşen bir mesaj gelene kadar bekleyecektir. Bir zaman aşımı da belirlenebilir. Örneğin,

receive do
   {:hello, msg}  -> msg
after
   1_000 -> "nothing after 1s"
end

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

nothing after 1s

NOTE - Mesajın posta kutusunda olmasını beklediğinizde zaman aşımı 0 verilebilir.

Bağlantılar

Elixir'de en yaygın yumurtlama şekli aslında yoluyladır. spawn_linkişlevi. Spawn_link ile bir örneğe bakmadan önce, bir süreç başarısız olduğunda ne olacağını anlayalım.

spawn fn -> raise "oops" end

Yukarıdaki program çalıştırıldığında, aşağıdaki hatayı üretir -

[error] Process #PID<0.58.00> raised an exception
** (RuntimeError) oops
   :erlang.apply/2

Bir hata günlüğe kaydetti, ancak yumurtlama işlemi hala çalışıyor. Bunun nedeni, süreçlerin izole edilmiş olmasıdır. Bir süreçteki başarısızlığın diğerine yayılmasını istiyorsak, onları bağlamamız gerekir. Bu, ile yapılabilirspawn_linkişlevi. Aynısını anlamak için bir örnek ele alalım -

spawn_link fn -> raise "oops" end

Yukarıdaki program çalıştırıldığında, aşağıdaki hatayı üretir -

** (EXIT from #PID<0.41.0>) an exception was raised:
   ** (RuntimeError) oops
      :erlang.apply/2

Eğer bunu çalıştırıyorsanız iexkabuk daha sonra kabuk bu hatayı işler ve çıkmaz. Ancak, önce bir komut dosyası oluşturup ardındanelixir <file-name>.exsBu başarısızlık nedeniyle ana süreç de durdurulacaktır.

Hataya dayanıklı sistemler oluştururken süreçler ve bağlantılar önemli bir rol oynar. Elixir uygulamalarında, süreçlerimizi genellikle bir sürecin ne zaman öldüğünü algılayacak ve onun yerine yeni bir süreç başlatacak olan denetçilerle ilişkilendiririz. Bu, yalnızca süreçlerin izole olması ve varsayılan olarak hiçbir şeyi paylaşmaması nedeniyle mümkündür. Ve süreçler izole edildiğinden, bir süreçteki bir arızanın bir diğerinin durumunu çökertmesi veya bozması mümkün değildir. Diğer diller istisnaları yakalamamızı / işlememizi gerektirecek olsa da; Elixir'de, süreçlerin başarısız olmasına izin vermekte aslında sorun yok çünkü denetçilerin sistemlerimizi düzgün bir şekilde yeniden başlatmasını bekliyoruz.

Durum

Örneğin, uygulama yapılandırmanızı korumak için durum gerektiren bir uygulama oluşturuyorsanız veya bir dosyayı ayrıştırıp bellekte tutmanız gerekiyorsa, onu nerede depolarsınız? Elixir'in süreç işlevselliği, bu tür şeyler yaparken kullanışlı olabilir.

Sonsuz döngü yapan, durumu koruyan ve mesaj gönderip alan süreçler yazabiliriz. Örnek olarak, adlı bir dosyada anahtar-değer deposu olarak çalışan yeni süreçleri başlatan bir modül yazalım.kv.exs.

defmodule KV do
   def start_link do
      Task.start_link(fn -> loop(%{}) end)
   end

   defp loop(map) do
      receive do
         {:get, key, caller} ->
         send caller, Map.get(map, key)
         loop(map)
         {:put, key, value} ->
         loop(Map.put(map, key, value))
      end
   end
end

Unutmayın ki start_link işlevi çalıştıran yeni bir işlem başlatır loopişlevi, boş bir haritayla başlayarak. loopişlevi daha sonra mesajları bekler ve her mesaj için uygun eylemi gerçekleştirir. Bir durumunda:getmesaj, arayana bir mesaj gönderir ve yeni bir mesaj beklemek için tekrar döngüyü arar. İken:put mesaj aslında çağırır loop haritanın yeni bir sürümü ile, verilen anahtar ve değer saklanır.

Şimdi aşağıdakileri çalıştıralım -

iex kv.exs

Şimdi senin içinde olmalısın iexkabuk. Modülümüzü test etmek için aşağıdakileri deneyin -

{:ok, pid} = KV.start_link

# pid now has the pid of our new process that is being 
# used to get and store key value pairs 

# Send a KV pair :hello, "Hello" to the process
send pid, {:put, :hello, "Hello"}

# Ask for the key :hello
send pid, {:get, :hello, self()}

# Print all the received messages on the current process.
flush()

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

"Hello"

Bu bölümde, metinsel temsillerle çalışmak için dilin sağladığı mekanizmalar olan işaretleri inceleyeceğiz. İşaretler tilde (~) karakteriyle başlar, ardından bir harf (bu işareti tanımlar) ve ardından bir sınırlayıcı gelir; isteğe bağlı olarak değiştiriciler son sınırlayıcıdan sonra eklenebilir.

Normal ifade

Elixir'deki normal ifadeler sigillerdir. Kullanımlarını String bölümünde gördük. Elixir'de regex'i nasıl kullanabileceğimizi görmek için tekrar bir örnek alalım.

# A regular expression that matches strings which contain "foo" or
# "bar":
regex = ~r/foo|bar/
IO.puts("foo" =~ regex)
IO.puts("baz" =~ regex)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

true
false

Mühürler 8 farklı sınırlayıcıyı destekler -

~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>

Farklı sınırlayıcıları desteklemenin arkasındaki neden, farklı sınırlayıcıların farklı işaretler için daha uygun olabilmesidir. Örneğin, normal ifadeler için parantez kullanmak, normal ifadenin içindeki parantezlerle karışabileceklerinden kafa karıştırıcı bir seçim olabilir. Ancak, bir sonraki bölümde göreceğimiz gibi, parantezler diğer işaretler için kullanışlı olabilir.

Elixir, Perl uyumlu regex'leri destekler ve ayrıca değiştiricileri destekler. Burada normal ifadelerin kullanımı hakkında daha fazla bilgi edinebilirsiniz .

Dizeler, Karakter listeleri ve Kelime listeleri

Düzenli ifadeler dışında Elixir'in 3 dahili işareti daha vardır. İşaretlere bir göz atalım.

Teller

~ S işareti, çift tırnak işaretleri gibi dizeler oluşturmak için kullanılır. ~ S işareti yararlıdır, örneğin, bir dizge hem çift hem de tek tırnak içerdiğinde -

new_string = ~s(this is a string with "double" quotes, not 'single' ones)
IO.puts(new_string)

Bu işaret, dizeleri oluşturur. Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

"this is a string with \"double\" quotes, not 'single' ones"

Karakter Listeleri

~ C işareti, karakter listeleri oluşturmak için kullanılır -

new_char_list = ~c(this is a char list containing 'single quotes')
IO.puts(new_char_list)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

this is a char list containing 'single quotes'

Kelime Listeleri

~ W işareti kelime listelerini oluşturmak için kullanılır (kelimeler sadece normal dizelerdir). ~ W işaretinin içinde, kelimeler boşluklarla ayrılır.

new_word_list = ~w(foo bar bat)
IO.puts(new_word_list)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

foobarbat

~ W işareti ayrıca c, s ve a sonuç listesinin öğelerinin veri türünü belirten değiştiriciler (sırasıyla karakter listeleri, dizeler ve atomlar için) -

new_atom_list = ~w(foo bar bat)a
IO.puts(new_atom_list)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

[:foo, :bar, :bat]

Mühürlerde İnterpolasyon ve Kaçış

Küçük harfli işaretlerin yanı sıra Elixir, kaçan karakterler ve enterpolasyonla başa çıkmak için büyük harfli işaretleri destekler. Hem ~ s hem de ~ S dizge döndürürken, ilki kaçış kodlarına ve enterpolasyona izin verirken ikincisi izin vermez. Bunu anlamak için bir örnek ele alalım -

~s(String with escape codes \x26 #{"inter" <> "polation"})
# "String with escape codes & interpolation"
~S(String without escape codes \x26 without #{interpolation})
# "String without escape codes \\x26 without \#{interpolation}"

Özel Mühürler

Kendi özel imzalarımızı kolayca oluşturabiliriz. Bu örnekte, bir dizeyi büyük harfe dönüştürmek için bir sigil oluşturacağız.

defmodule CustomSigil do
   def sigil_u(string, []), do: String.upcase(string)
end

import CustomSigil

IO.puts(~u/tutorials point/)

Yukarıdaki kodu çalıştırdığımızda şu sonucu verir -

TUTORIALS POINT

Önce CustomSigil adında bir modül tanımlıyoruz ve bu modül içinde sigil_u adında bir fonksiyon oluşturduk. Mevcut sigil uzayında mevcut ~ u sigil olmadığı için onu kullanacağız. _U, tilde'dan sonra u karakterini kullanmak istediğimizi belirtir. İşlev tanımı iki argüman, bir girdi ve bir liste almalıdır.

Liste anlayışları, Elixir'deki numaralandırılabilir öğeler arasında döngü yapmak için sözdizimsel şekerdir. Bu bölümde, yineleme ve oluşturma için anlamaları kullanacağız.

Temel bilgiler

Numaralandırmalar bölümünde Enum modülüne baktığımızda, map fonksiyonuyla karşılaştık.

Enum.map(1..3, &(&1 * 2))

Bu örnekte, ikinci argüman olarak bir fonksiyonu ileteceğiz. Aralıktaki her öğe işleve aktarılacak ve ardından yeni değerleri içeren yeni bir liste döndürülecektir.

Eşleme, filtreleme ve dönüştürme Elixir'de çok yaygın eylemlerdir ve bu nedenle, önceki örnekten aynı sonucu elde etmenin biraz farklı bir yolu vardır -

for n <- 1..3, do: n * 2

Yukarıdaki kodu çalıştırdığımızda şu sonucu verir -

[2, 4, 6]

İkinci örnek bir kavramadır ve muhtemelen görebileceğiniz gibi, basitçe sözdizimsel şekerdir, aynı zamanda şunu kullanırsanız elde edebileceğiniz şeydir. Enum.mapişlevi. Bununla birlikte, Enum modülünden bir işlev üzerinde bir kavrayış kullanmanın performans açısından gerçek bir faydası yoktur.

Anlamalar listelerle sınırlı değildir, tüm numaralandırılabilir öğelerle kullanılabilir.

Filtrele

Filtreleri anlamak için bir tür koruma olarak düşünebilirsiniz. Filtrelenmiş bir değer döndüğündefalse veya nilnihai listeden çıkarılır. Bir aralığı gözden geçirelim ve yalnızca çift sayılar için endişelenelim. Kullanacağızis_even Tamsayı modülünden bir değerin eşit olup olmadığını kontrol etmek için işlev.

import Integer
IO.puts(for x <- 1..10, is_even(x), do: x)

Yukarıdaki kod çalıştırıldığında, aşağıdaki sonucu verir -

[2, 4, 6, 8, 10]

Aynı anlayış içinde birden fazla filtre de kullanabiliriz. Sonra istediğiniz başka bir filtreyi ekleyin.is_even virgülle ayrılmış filtre.

: Seçeneğe

Yukarıdaki örneklerde, tüm anlamalar sonuç olarak listeler döndürmüştür. Bununla birlikte, bir kavrayışın sonucu, geçerek farklı veri yapılarına eklenebilir.:into anlama seçeneği.

Örneğin, bir bitstring oluşturucu, bir dizedeki tüm boşlukları kolayca kaldırmak için: into seçeneğiyle birlikte kullanılabilir -

IO.puts(for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>)

Yukarıdaki kod çalıştırıldığında, aşağıdaki sonucu verir -

helloworld

Yukarıdaki kod, şunu kullanarak dizedeki tüm boşlukları kaldırır c != ?\s filtreleyin ve sonra: into seçeneğini kullanarak, döndürülen tüm karakterleri bir dizeye yerleştirir.

Elixir, dinamik olarak yazılmış bir dildir, bu nedenle Elixir'deki tüm türler çalışma zamanına göre çıkarılır. Bununla birlikte, Elixir için kullanılan bir gösterim olan tür özellikleriyle birlikte gelir.declaring custom data types and declaring typed function signatures (specifications).

İşlev Özellikleri (özellikler)

Elixir varsayılan olarak tamsayı veya pid gibi bazı temel türleri ve ayrıca karmaşık türleri sağlar: örneğin, roundbir kayan noktayı en yakın tam sayıya yuvarlayan işlev, bir sayıyı bağımsız değişken olarak (bir tamsayı veya bir yüzer sayı) alır ve bir tamsayı döndürür. İlgili dokümantasyonda yuvarlak tip imza şu şekilde yazılmıştır:

round(number) :: integer

Yukarıdaki açıklama, soldaki fonksiyonun parantez içinde belirtilen şeyi bağımsız değişken olarak aldığını ve :: 'nin sağında olanı, yani Tamsayı döndürdüğünü ima eder. Fonksiyon özellikleri ile yazılır@specyönerge, işlev tanımının hemen önüne yerleştirilir. Yuvarlak işlevi şu şekilde yazılabilir -

@spec round(number) :: integer
def round(number), do: # Function implementation
...

Tür belirtimleri, karmaşık türleri de destekler; örneğin, bir tam sayı listesi döndürmek istiyorsanız, [Integer]

Özel Tipler

Elixir birçok yararlı dahili tür sağlarken, uygun olduğunda özel türleri tanımlamak uygundur. Bu, @type yönergesi aracılığıyla modülleri tanımlarken yapılabilir. Aynısını anlamak için bir örnek ele alalım -

defmodule FunnyCalculator do
   @type number_with_joke :: {number, String.t}

   @spec add(number, number) :: number_with_joke
   def add(x, y), do: {x + y, "You need a calculator to do that?"}

   @spec multiply(number, number) :: number_with_joke
   def multiply(x, y), do: {x * y, "It is like addition on steroids."}
end

{result, comment} = FunnyCalculator.add(10, 20)
IO.puts(result)
IO.puts(comment)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

30
You need a calculator to do that?

NOTE - @type aracılığıyla tanımlanan özel türler dışa aktarılır ve içinde tanımlandıkları modülün dışında kullanılabilir. Özel bir türü özel tutmak istiyorsanız, @typep yerine yönerge @type.

Elixir'deki (ve Erlang'daki) davranışlar, bir bileşenin genel bölümünü (davranış modülü haline gelen) belirli bir parçadan (geri arama modülü haline gelen) ayırmanın ve soyutlamanın bir yoludur. Davranışlar bir yol sağlar -

  • Bir modül tarafından uygulanması gereken bir dizi işlevi tanımlayın.
  • Bir modülün o setteki tüm fonksiyonları uyguladığından emin olun.

Gerekirse, Java gibi nesne yönelimli dillerdeki arabirimler gibi davranışlar düşünebilirsiniz: bir modülün uygulaması gereken bir dizi işlev imzası.

Bir Davranışı Tanımlamak

Kendi davranışımızı yaratmak için bir örnek düşünelim ve sonra bu genel davranışı bir modül oluşturmak için kullanalım. İnsanları farklı dillerde merhaba ve hoşçakal selamlayan bir davranış tanımlayacağız.

defmodule GreetBehaviour do
   @callback say_hello(name :: string) :: nil
   @callback say_bye(name :: string) :: nil
end

@callbackyönergesi, modülleri benimseyenlerin tanımlaması gereken işlevleri listelemek için kullanılır. Ayrıca no belirtir. değişkenler, türleri ve dönüş değerleri.

Bir Davranış benimsemek

Bir davranışı başarıyla tanımladık. Şimdi bunu birden çok modülde benimseyip uygulayacağız. Bu davranışı İngilizce ve İspanyolca olarak uygulayan iki modül oluşturalım.

defmodule GreetBehaviour do
   @callback say_hello(name :: string) :: nil
   @callback say_bye(name :: string) :: nil
end

defmodule EnglishGreet do
   @behaviour GreetBehaviour
   def say_hello(name), do: IO.puts("Hello " <> name)
   def say_bye(name), do: IO.puts("Goodbye, " <> name)
end

defmodule SpanishGreet do
   @behaviour GreetBehaviour
   def say_hello(name), do: IO.puts("Hola " <> name)
   def say_bye(name), do: IO.puts("Adios " <> name)
end

EnglishGreet.say_hello("Ayush")
EnglishGreet.say_bye("Ayush")
SpanishGreet.say_hello("Ayush")
SpanishGreet.say_bye("Ayush")

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hello Ayush
Goodbye, Ayush
Hola Ayush
Adios Ayush

Daha önce görmüş olduğunuz gibi, @behaviourmodüldeki yönerge. Tüm alt modüller için davranışta uygulanan tüm işlevleri tanımlamalıyız . Bu, kabaca OOP dillerindeki arayüzlere eşdeğer olarak düşünülebilir.

Elixir'in üç hata mekanizması vardır: hatalar, fırlatmalar ve çıkışlar. Her mekanizmayı ayrıntılı olarak inceleyelim.

Hata

Kodda istisnai şeyler olduğunda hatalar (veya istisnalar) kullanılır. Bir dizgeye bir sayı eklenmeye çalışılarak örnek bir hata alınabilir -

IO.puts(1 + "Hello")

Yukarıdaki program çalıştırıldığında, aşağıdaki hatayı üretir -

** (ArithmeticError) bad argument in arithmetic expression
   :erlang.+(1, "Hello")

Bu dahili bir örnek hataydı.

Hata Oluşturma

Yapabiliriz raiseyükseltme işlevlerini kullanan hatalar. Aynısını anlamak için bir örnek ele alalım -

#Runtime Error with just a message
raise "oops"  # ** (RuntimeError) oops

Yükseltme / 2 hata adı ve anahtar kelime argümanları listesi geçildiğinde başka hatalar ortaya çıkabilir.

#Other error type with a message
raise ArgumentError, message: "invalid argument foo"

Ayrıca kendi hatalarınızı tanımlayabilir ve bunları artırabilirsiniz. Şu örneği düşünün -

defmodule MyError do
   defexception message: "default message"
end

raise MyError  # Raises error with default message
raise MyError, message: "custom message"  # Raises error with custom message

Hataları Kurtarmak

Programlarımızın aniden kapanmasını istemiyoruz, bunun yerine hataların dikkatlice ele alınması gerekiyor. Bunun için hata işlemeyi kullanıyoruz. Bizrescue kullanarak hatalar try/rescueinşa etmek. Aynısını anlamak için aşağıdaki örneği ele alalım -

err = try do
   raise "oops"
rescue
   e in RuntimeError -> e
end

IO.puts(err.message)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

oops

Rescue ifadesindeki hataları örüntü eşleştirmeyi kullanarak ele aldık. Hatayı kullanmazsak ve sadece tanımlama amacıyla kullanmak istiyorsak, formu da kullanabiliriz -

err = try do
   1 + "Hello"
rescue
   RuntimeError -> "You've got a runtime error!"
   ArithmeticError -> "You've got a Argument error!"
end

IO.puts(err)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

You've got a Argument error!

NOTE- Elixir standart kitaplığındaki çoğu işlev, bir kez tuple döndüren ve diğer zaman yükseltme hataları olmak üzere iki kez uygulanır. Örneğin,File.read ve File.read!fonksiyonlar. İlki, dosya başarılı bir şekilde okunduysa bir demet döndürdü ve bir hatayla karşılaşılırsa, bu demet hatanın nedenini belirtmek için kullanıldı. İkincisi, bir hatayla karşılaşıldığında bir hatayı ortaya çıkardı.

İlk fonksiyon yaklaşımını kullanırsak, hatayı örüntü eşleştirmek için durum kullanmalı ve buna göre işlem yapmalıyız. İkinci durumda, hataya meyilli kod için kurtarmayı dene yaklaşımını kullanıyoruz ve hataları buna göre ele alıyoruz.

Atar

İksir'de bir değer atılabilir ve daha sonra yakalanabilir. Fırlatma ve Yakalama, fırlatma ve yakalama kullanılmadıkça bir değer almanın mümkün olmadığı durumlar için ayrılmıştır.

Örnekler, kitaplıklarla arabirim oluşturma dışında pratikte oldukça nadirdir. Örneğin, şimdi Enum modülünün bir değer bulmak için herhangi bir API sağlamadığını ve bir sayılar listesinde 13'ün ilk katını bulmamız gerektiğini varsayalım -

val = try do
   Enum.each 20..100, fn(x) ->
      if rem(x, 13) == 0, do: throw(x)
   end
   "Got nothing"
catch
   x -> "Got #{x}"
end

IO.puts(val)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Got 26

çıkış

Bir süreç “doğal nedenlerden” öldüğünde (örneğin, işlenmemiş istisnalar), bir çıkış sinyali gönderir. Bir işlem ayrıca açıkça bir çıkış sinyali göndererek de ölebilir. Şu örneği ele alalım -

spawn_link fn -> exit(1) end

Yukarıdaki örnekte, bağlantılı süreç 1 değerinde bir çıkış sinyali göndererek öldü. Çıkışın dene / yakala kullanılarak da "yakalanabileceğini" unutmayın. Örneğin -

val = try do
   exit "I am exiting"
catch
   :exit, _ -> "not really"
end

IO.puts(val)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

not really

Sonra

Bazen, bir hataya yol açabilecek bazı işlemlerden sonra bir kaynağın temizlenmesini sağlamak gerekir. Try / after yapısı bunu yapmanıza izin verir. Örneğin, bir dosyayı açabilir ve bir şeyler ters gitse bile kapatmak için after cümlesini kullanabiliriz.

{:ok, file} = File.open "sample", [:utf8, :write]
try do
   IO.write file, "olá"
   raise "oops, something went wrong"
after
   File.close(file)
end

Bu programı çalıştırdığımızda bize bir hata verecektir. Fakatafter ifadesi, bu tür herhangi bir olayda dosya tanımlayıcısının kapatılmasını sağlayacaktır.

Makrolar, Elixir'in en gelişmiş ve güçlü özelliklerinden biridir. Herhangi bir dilin tüm gelişmiş özelliklerinde olduğu gibi, makrolar idareli kullanılmalıdır. Derleme zamanında güçlü kod dönüşümleri gerçekleştirmeyi mümkün kılarlar. Şimdi makroların ne olduğunu ve bunların nasıl kullanılacağını kısaca anlayacağız.

Alıntı

Makrolar hakkında konuşmaya başlamadan önce, önce Elixir'in iç bileşenlerine bakalım. Elixir programı kendi veri yapılarıyla temsil edilebilir. Bir Elixir programının yapı taşı, üç unsuru olan bir gruptur. Örneğin, fonksiyon çağrısı sum (1, 2, 3) dahili olarak şu şekilde temsil edilir:

{:sum, [], [1, 2, 3]}

İlk öğe işlev adı, ikincisi meta verileri içeren bir anahtar sözcük listesi ve üçüncüsü bağımsız değişkenler listesidir. Aşağıdakileri yazarsanız, bunu iex shell'de çıktı olarak alabilirsiniz -

quote do: sum(1, 2, 3)

Operatörler de bu tür tuple olarak temsil edilir. Değişkenler, son elemanın bir liste yerine bir atom olması dışında, bu tür üçlüler kullanılarak da temsil edilir. Daha karmaşık ifadelerden alıntı yaparken, kodun genellikle bir ağaca benzeyen bir yapıda iç içe geçmiş bu tür tuplelar içinde temsil edildiğini görebiliriz. Birçok dil, bu tür temsilleri birAbstract Syntax Tree (AST). Elixir bu alıntılanmış ifadeleri çağırır.

Alıntıyı kaldır

Artık kodumuzun iç yapısını alabildiğimize göre, onu nasıl değiştirebiliriz? Yeni kod veya değerler enjekte etmek için kullanırızunquote. Bir ifadeyi alıntı yaptığımızda değerlendirilecek ve AST'ye enjekte edilecektir. Kavramı anlamak için bir örnek (iex shell'de) ele alalım -

num = 25

quote do: sum(15, num)

quote do: sum(15, unquote(num))

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

{:sum, [], [15, {:num, [], Elixir}]}
{:sum, [], [15, 25]}

Alıntı ifadesi örneğinde, num otomatik olarak 25 ile değiştirilmemiştir. AST'yi değiştirmek istiyorsak bu değişkeni alıntı yapmamız gerekir.

Makrolar

Artık alıntı ve alıntıya aşina olduğumuza göre Elixir'de makro programlamayı inceleyebiliriz.

En basit terimlerle makrolar, uygulama kodumuza eklenecek alıntılanmış bir ifade döndürmek için tasarlanmış özel işlevlerdir. Makronun bir işlev olarak adlandırılmak yerine, alıntılanan ifadeyle değiştirildiğini hayal edin. Makrolarla Elixir'i genişletmek ve uygulamalarımıza dinamik olarak kod eklemek için gereken her şeye sahibiz

Makro olmadıkça uygulayalım. Makroyu kullanarak tanımlayarak başlayacağız.defmacromakro. Makromuzun alıntılanmış bir ifade döndürmesi gerektiğini unutmayın.

defmodule OurMacro do
   defmacro unless(expr, do: block) do
      quote do
         if !unquote(expr), do: unquote(block)
      end
   end
end

require OurMacro

OurMacro.unless true, do: IO.puts "True Expression"

OurMacro.unless false, do: IO.puts "False expression"

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

False expression

Burada olan şey, kodumuzun, never makrosu tarafından döndürülen alıntılanan kodla değiştirilmesidir . İfadeyi mevcut bağlamda değerlendirmek için alıntıdan çıkardık ve ayrıca kendi bağlamında yürütmek için do bloğundan alıntı yaptık. Bu örnek bize iksirdeki makroları kullanarak metaprogramlamayı gösteriyor.

Makrolar çok daha karmaşık görevlerde kullanılabilir, ancak idareli kullanılmalıdır. Bunun nedeni, genel olarak meta programlamanın kötü bir uygulama olarak görülmesi ve yalnızca gerekli olduğunda kullanılması gerektiğidir.

Elixir, Erlang kitaplıkları ile mükemmel birlikte çalışabilirlik sağlar. Birkaç kütüphaneyi kısaca tartışalım.

İkili Modül

Yerleşik Elixir String modülü UTF-8 kodlu ikili dosyaları işler. İkili modül, UTF-8 kodlaması gerekmeyen ikili verilerle uğraşırken kullanışlıdır. İkili modülü daha iyi anlamak için bir örnek ele alalım -

# UTF-8
IO.puts(String.to_char_list("Ø"))

# binary
IO.puts(:binary.bin_to_list "Ø")

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

[216]
[195, 152]

Yukarıdaki örnek farkı göstermektedir; String modülü UTF-8 kod noktalarını döndürürken: ikili, ham veri baytlarıyla ilgilenir.

Kripto Modülü

Kripto modülü, karma işlevler, dijital imzalar, şifreleme ve daha fazlasını içerir. Bu modül, Erlang standart kitaplığının bir parçası değildir, ancak Erlang dağıtımına dahildir. Bu, şunları listelemeniz gerektiği anlamına gelir: kriptoyu her kullandığınızda projenizin uygulamalar listesinde. Kripto modülünü kullanarak bir örnek görelim -

IO.puts(Base.encode16(:crypto.hash(:sha256, "Elixir")))

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

3315715A7A3AD57428298676C5AE465DADA38D951BDFAC9348A8A31E9C7401CB

Digraph Modülü

Digraph modülü, köşelerden ve kenarlardan oluşan yönlendirilmiş grafiklerle uğraşmak için işlevler içerir. Grafiği oluşturduktan sonra, oradaki algoritmalar, örneğin, iki köşe arasındaki en kısa yolu veya grafikteki döngüleri bulmaya yardımcı olacaktır. Unutmayın ki fonksiyonlarin :digraph Eklenen köşeleri veya kenarları döndürürken grafik yapısını bir yan etki olarak dolaylı olarak değiştirin.

digraph = :digraph.new()
coords = [{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}]
[v0, v1, v2] = (for c <- coords, do: :digraph.add_vertex(digraph, c))
:digraph.add_edge(digraph, v0, v1)
:digraph.add_edge(digraph, v1, v2)
for point <- :digraph.get_short_path(digraph, v0, v2) do 
   {x, y} = point
   IO.puts("#{x}, #{y}")
end

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

0.0, 0.0
1.0, 0.0
1.0, 1.0

Matematik Modülü

Matematik modülü, trigonometri, üstel ve logaritmik fonksiyonları kapsayan yaygın matematiksel işlemleri içerir. Math modülünün nasıl çalıştığını anlamak için aşağıdaki örneği ele alalım -

# Value of pi
IO.puts(:math.pi())

# Logarithm
IO.puts(:math.log(7.694785265142018e23))

# Exponentiation
IO.puts(:math.exp(55.0))

#...

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

3.141592653589793
55.0
7.694785265142018e23

Kuyruk Modülü

Kuyruk, FIFO (ilk giren ilk çıkar) kuyruklarını verimli bir şekilde uygulayan (çift uçlu) bir veri yapısıdır. Aşağıdaki örnek, Kuyruk modülünün nasıl çalıştığını gösterir -

q = :queue.new
q = :queue.in("A", q)
q = :queue.in("B", q)
{{:value, val}, q} = :queue.out(q)
IO.puts(val)
{{:value, val}, q} = :queue.out(q)
IO.puts(val)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

A
B