Elixir 1.7

Recursion




elixir

Recursion

पुनरावृत्ति के माध्यम से लूप

अपरिवर्तनीयता के कारण, एलिक्सिर में लूप्स (किसी भी कार्यात्मक प्रोग्रामिंग भाषा में) अलग-अलग भाषाओं में लिखे गए हैं। उदाहरण के लिए, C जैसी अनिवार्य भाषा में, कोई भी लिखेगा:

for(i = 0; i < sizeof(array); i++) {
  array[i] = array[i] * 2;
}

ऊपर के उदाहरण में, हम सरणी और चर दोनों को परिवर्तित कर रहे हैं i । अमृत ​​में संभव नहीं है। इसके बजाय, कार्यात्मक भाषाएं पुनरावृत्ति पर निर्भर करती हैं: एक फ़ंक्शन को पुनरावर्ती कहा जाता है जब तक कि एक स्थिति तक नहीं पहुंच जाती है जो पुनरावर्ती कार्रवाई को जारी रखने से रोकती है। इस प्रक्रिया में कोई डेटा म्यूट नहीं किया गया है। नीचे दिए गए उदाहरण पर विचार करें कि एक स्ट्रिंग एक मनमानी संख्या को प्रिंट करती है:

defmodule Recursion 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

Recursion.print_multiple_times("Hello!", 3)
# Hello!
# Hello!
# Hello!

case समान, एक फ़ंक्शन में कई खंड हो सकते हैं। किसी विशेष खंड को निष्पादित किया जाता है जब फ़ंक्शन के लिए दिए गए तर्क क्लाज के तर्क पैटर्न से मेल खाते हैं और इसका गार्ड true मूल्यांकन करता true

जब print_multiple_times/2 को शुरू में ऊपर के उदाहरण में कहा जाता है, तो तर्क n 3 बराबर है।

पहले क्लॉज में एक गार्ड होता है जो कहता है कि "इस परिभाषा का उपयोग करें यदि और केवल यदि n 1 से कम या बराबर है"। चूंकि यह मामला नहीं है, अमृत अगले खंड की परिभाषा के लिए आगे बढ़ता है।

दूसरी परिभाषा पैटर्न से मेल खाती है और इसमें कोई गार्ड नहीं है इसलिए इसे निष्पादित किया जाएगा। यह पहले हमारे msg प्रिंट करता है और फिर दूसरे तर्क के रूप में n - 1 ( 2 ) पास करने को कहता है।

हमारा msg प्रिंट होता है और print_multiple_times/2 को फिर से बुलाया जाता है, इस बार दूसरे तर्क के साथ 1 सेट किया गया है। क्योंकि n अब 1 सेट हो गया है, हमारे print_multiple_times/2 की पहली परिभाषा में गार्ड सही का मूल्यांकन करता है, और हम इस विशेष परिभाषा को निष्पादित करते हैं। msg मुद्रित किया जाता है, और निष्पादित करने के लिए कुछ भी नहीं बचा है।

हमने print_multiple_times/2 परिभाषित किया है, ताकि कोई भी संख्या दूसरे तर्क के रूप में पारित न हो, यह या तो हमारी पहली परिभाषा को ट्रिगर करता है (जिसे आधार मामले के रूप में जाना जाता है) या यह हमारी दूसरी परिभाषा को ट्रिगर करता है, जो यह सुनिश्चित करेगा कि हम वास्तव में एक के करीब पहुंचें हमारा आधार मामला।

एल्गोरिदम को कम और मैप करें

आइए अब देखें कि हम संख्या की सूची का योग करने के लिए पुनरावर्तन की शक्ति का उपयोग कैसे कर सकते हैं:

defmodule Math do
  def sum_list([head | tail], accumulator) do
    sum_list(tail, head + accumulator)
  end

  def sum_list([], accumulator) do
    accumulator
  end
end

IO.puts Math.sum_list([1, 2, 3], 0) #=> 6

हम सूची के साथ sum_list [1, 2, 3] और प्रारंभिक मान 0 को तर्क के रूप में आमंत्रित करते हैं। हम प्रत्येक क्लॉज को तब तक आजमाएंगे जब तक हमें एक ऐसा नहीं मिल जाता है जो पैटर्न मिलान नियमों के अनुसार मेल खाता है। इस मामले में, सूची [1, 2, 3] मुकाबले [head | tail] [head | tail] जो head को 1 से बांधती है और tail को [2, 3] ; accumulator 0 सेट है।

फिर, हम सूची के head + accumulator को संचायक के head + accumulator sum_list और फिर से, पहले की तरह सूची की पूंछ को पास करते हुए, sum_list फिर से कॉल sum_list । पूंछ एक बार फिर से मेल खाएगी [head | tail] [head | tail] जब तक सूची खाली न हो, जैसा कि नीचे देखा गया है:

sum_list [1, 2, 3], 0
sum_list [2, 3], 1
sum_list [3], 3
sum_list [], 6

जब सूची खाली है, तो यह अंतिम क्लॉज से मेल खाएगा जो 6 का अंतिम परिणाम देता है।

एक सूची लेने और इसे एक मूल्य तक कम करने की प्रक्रिया को कम एल्गोरिदम के रूप में जाना जाता है और कार्यात्मक प्रोग्रामिंग के लिए केंद्रीय है।

क्या होगा अगर हम इसके बजाय अपनी सूची के सभी मूल्यों को दोगुना करना चाहते हैं?

defmodule Math do
  def double_each([head | tail]) do
    [head * 2 | double_each(tail)]
  end

  def double_each([]) do
    []
  end
end
$ iex math.exs
iex> Math.double_each([1, 2, 3]) #=> [2, 4, 6]

यहां हमने एक सूची को पार करने के लिए, प्रत्येक तत्व को दोगुना करने और एक नई सूची वापस करने के लिए पुनरावर्तन का उपयोग किया है। एक सूची लेने और उस पर मानचित्रण करने की प्रक्रिया को मानचित्र एल्गोरिथ्म के रूप में जाना जाता है

पुनरावर्तन और पूंछ कॉल अनुकूलन एलिक्सिर का एक महत्वपूर्ण हिस्सा है और आमतौर पर लूप बनाने के लिए उपयोग किया जाता है। हालाँकि, जब एलिक्सिर में प्रोग्रामिंग करते हैं, तो आप शायद ही कभी सूची में हेरफेर करने के लिए ऊपर के रूप में पुनरावृत्ति का उपयोग करेंगे।

Enum मॉड्यूल , जिसे हम अगले अध्याय में देखने जा रहे हैं, पहले से ही सूचियों के साथ काम करने के लिए कई उपयुक्तताएं प्रदान करता है। उदाहरण के लिए, ऊपर दिए गए उदाहरण इस प्रकार लिखे जा सकते हैं:

iex> Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)
6
iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
[2, 4, 6]

या, कैप्चर सिंटैक्स का उपयोग कर:

iex> Enum.reduce([1, 2, 3], 0, &+/2)
6
iex> Enum.map([1, 2, 3], &(&1 * 2))
[2, 4, 6]

चलिए Enumerable s पर एक गहरी नज़र डालते हैं और जब हम इस पर होते हैं, तो उनके आलसी समकक्ष, Stream एस।