পুঙ্খানুপুঙ্খ সম্পত্তিগুলির মধ্যে পুচ্ছ পুনরাবৃত্তি মডুলো কনস নির্মূলের অনুমতি দেয়?


14

আমি বেসিক লেজ পুনরাবৃত্তি নির্মূলকরণের ধারণার সাথে পরিচিত , যেখানে কোনও কলের প্রত্যক্ষ ফলাফল নিজের কাছে ফেরত ফাংশনগুলি পুনরাবৃত্ত লুপ হিসাবে পুনরায় লেখা যেতে পারে।

foo(...):
    # ...
    return foo(...)

আমি এটিও বুঝতে পেরেছি, একটি বিশেষ ক্ষেত্রে হিসাবে, পুনরাবৃত্ত কলটি একটি কলটিতে আবৃত করা থাকলে ফাংশনটি আবারও লেখা যেতে পারে cons

foo(...):
    # ...
    return (..., foo(...))

কোন সম্পত্তি consএই অনুমতি দেয়? consপুনরাবৃত্তভাবে পুনরায় লেখার আমাদের ক্ষমতাকে বিনষ্ট না করে একটি পুনরাবৃত্ত টেল কলটি মোড়ানো করতে পারে এমন ব্যতীত অন্য কোন কাজ ?

জিসিসি (তবে ঝনঝন নয়) "লেজ পুনরাবৃত্তি মডুলো গুণন " এর এই উদাহরণটি অনুকূল করতে সক্ষম হয়েছে তবে এটি কোনটি কীভাবে এটি আবিষ্কার করতে সহায়তা করে বা কীভাবে এটি এর রূপান্তর ঘটায় তা পরিষ্কার নয়।

pow(x, n):
    if n == 0: return 1
    else if n == 1: return x
    else: return x * pow(x, n-1)

1
আপনার গডবোল্ট সংকলক এক্সপ্লোরার লিঙ্কে, আপনার ফাংশনটি রয়েছে if(n==0) return 0;(আপনার প্রশ্নের মতো 1 টি ফেরেনি )। x^0 = 1সুতরাং, এটি একটি বাগ। যদিও এটি বাকি প্রশ্নের জন্য গুরুত্বপূর্ণ, তবে; পুনরাবৃত্তির asm প্রথমে সেই বিশেষ ক্ষেত্রে পরীক্ষা করে। তবে আশ্চর্যের বিষয় হল, পুনরাবৃত্তি বাস্তবায়ন উত্সটিতে 1 * xউপস্থিত ছিল না এমন একটি গুণকে পরিচয় করিয়ে দেয়, এমনকি আমরা একটি floatসংস্করণ তৈরি করি। gcc.godbolt.org/z/eqwine (এবং জিসিসি কেবল এতেই সফল হয় -ffast-math))
পিটার

পছন্দ করুন return 0সংশোধন করা হয়েছে। 1 দ্বারা গুণটি আকর্ষণীয়। আমি কী করব তা নিশ্চিত নই।
ম্যাক্সপাম

আমি মনে করি এটি লুপে রূপান্তরিত করার সময় এটি জিসিসি যেভাবে রূপান্তরিত হয় তার একটি পার্শ্ব-প্রতিক্রিয়া। স্পষ্টতই এখানে জিসিসির কিছু মিসড অপটিমাইজেশন রয়েছে, যেমন এটি floatবিনা জন্য মিস করা -ffast-math, যদিও এটি প্রতিবার একই মানের হয় multip (1.0f` ব্যতীত যা সম্ভবত স্টিকিং পয়েন্ট হতে পারে?)
পিটার

উত্তর:


12

জিসিসি সম্ভবত অ্যাডহক নিয়ম ব্যবহার করে, আপনি নিম্নলিখিত উপায়ে সেগুলি অর্জন করতে পারেন। আমি powচিত্রিত করতে ব্যবহার করব যেহেতু আপনি fooএতটা অস্পষ্টভাবে সংজ্ঞায়িত হয়েছেন । এছাড়াও, fooভাষা ওজে যেমন ভাষা ওজ রয়েছে এবং কম্পিউটার প্রোগ্রামিংয়ের ধারণাগুলি, কৌশল এবং মডেলগুলিতে যেমন আলোচনা করা হয়েছে তেমনি একক-কার্যকরী ভেরিয়েবলের ক্ষেত্রে সর্বশেষ কলের অপ্টিমাইজেশনের উদাহরণ হিসাবে সবচেয়ে ভাল বোঝা যাবে । একক-অ্যাসাইনমেন্ট ভেরিয়েবলগুলি ব্যবহারের সুবিধা হ'ল এটি একটি ঘোষণামূলক প্রোগ্রামিং প্যারাডিজমের মধ্যে থাকতে দেয়। মূলত, আপনার স্ট্রাক্ট fooরিটার্নের প্রতিটি ক্ষেত্র একক-অ্যাসাইনমেন্ট ভেরিয়েবল দ্বারা প্রতিনিধিত্ব করতে পারে যা পরে fooঅতিরিক্ত আর্গুমেন্ট হিসাবে প্রেরণ করা হয়। fooতারপরে একটি লেজ পুনরুক্তি হয়ে যায়voidফাংশন ফিরে। এর জন্য কোনও বিশেষ বুদ্ধিমানের প্রয়োজন নেই।

powপ্রথমে ফিরে আসা , ধারাবাহিকতা পাস করার শৈলীতে রূপান্তর । powহয়ে:

pow(x, n):
    return pow2(x, n, x => x)

pow2(x, n, k):
    if n == 0: return k(1)
    else if n == 1: return k(x)
    else: return pow2(x, n-1, y => k(x*y))

সমস্ত কল এখন টেল কল। যাইহোক, নিয়ন্ত্রণ স্ট্যাকটি ধারাবাহিকতার প্রতিনিধিত্ব করে বন্ধগুলিতে বন্দী পরিবেশে সরানো হয়েছে।

এরপরে, ধারাবাহিকতাগুলিকে ডিফোনশিয়াল করুন। যেহেতু কেবলমাত্র একটি পুনরাবৃত্ত কল রয়েছে, ফলস্বরূপ অব্যাহত ধারাবাহিকতার প্রতিনিধিত্বকারী ফলাফল ডেটা কাঠামো একটি তালিকা। আমরা পেতে:

pow(x, n):
    return pow2(x, n, Nil)

pow2(x, n, k):
    if n == 0: return applyPow(k, 1)
    else if n == 1: return applyPow(k, x)
    else: return pow2(x, n-1, Cons(x, k))

applyPow(k, acc):
    match k with:
        case Nil: return acc
        case Cons(x, k):
            return applyPow(k, x*acc)

কী applyPow(k, acc)করে একটি তালিকা গ্রহণ করা, অর্থাত্ মুক্ত মনোয়েড, পছন্দ k=Cons(x, Cons(x, Cons(x, Nil)))করে এটিকে তৈরি করা x*(x*(x*acc))। তবে যেহেতু সাহচর্যমূলক *এবং সাধারণত ইউনিটের সাথে 1একঘেয়েমি তৈরি করে, তাই আমরা এটিকে পুনরায় সহযোগিতা করতে পারি ((x*x)*x)*acc, এবং সরলতার জন্য 1, উত্পাদন শুরু করতে চেষ্টা করি (((1*x)*x)*x)*acc। মূল কথাটি হ'ল আমরা আসলে আংশিকভাবে ফলাফলটি আগে থেকেই গণনা করতে পারি acc। এর অর্থ kএকটি তালিকা হিসাবে ঘুরে বেড়াবার পরিবর্তে মূলত কিছু অসম্পূর্ণ "বাক্য গঠন" যা আমরা শেষে "ব্যাখ্যা করব", আমরা যেতে যেতে "ব্যাখ্যা" করতে পারি। ফলশ্রুতিটি হ'ল আমরা এই ক্ষেত্রে Nilমনোয়েডের ইউনিটটি প্রতিস্থাপন করতে পারি এবং মনোয়েডের ক্রিয়াকলাপটি ব্যবহার করতে পারি এবং এখন "চলমান পণ্য" উপস্থাপন করি।1Cons*kapplyPow(k, acc)তারপরে ঠিক হয়ে যায় k*accযা আমরা উত্পাদনটি আবার ইনলাইন করতে pow2এবং সহজতর করতে পারি :

pow(x, n):
    return pow2(x, n, 1)

pow2(x, n, k):
    if n == 0: return k
    else if n == 1: return k*x
    else: return pow2(x, n-1, k*x)

মূলটির একটি পুচ্ছ-পুনরাবৃত্তি, সঞ্চায়ক-পাসিং শৈলী সংস্করণ pow

অবশ্যই, আমি বলছি না যে সংকলন সময়ে জিসিসি এই সমস্ত যুক্তি করে does জিসিসি কোন যুক্তি ব্যবহার করে তা আমি জানি না। আমার বক্তব্যটি কেবল একবার এই যুক্তিটি সম্পাদন করে চলেছে, প্যাটার্নটি সনাক্ত করা এবং অবিলম্বে এই চূড়ান্ত ফর্মটিতে মূল উত্স কোডটি অনুবাদ করা তুলনামূলকভাবে সহজ। যাইহোক, সিপিএস রূপান্তর এবং সংশোধন রূপান্তর সম্পূর্ণ সাধারণ এবং যান্ত্রিক। সেখান থেকে সংশোধিত ধারাবাহিকতাগুলি অপসারণের জন্য ফিউশন, বন উজাড় করা বা সুপার সংকলন কৌশল ব্যবহার করা যেতে পারে। পুনরায় সংশোধিত ধারাবাহিকতার সমস্ত বরাদ্দ অপসারণ করা সম্ভব না হলে অনুমানমূলক রূপান্তরগুলি ফেলে দেওয়া যেতে পারে। আমি সন্দেহ করি, যদিও পুরো সময়টি পুরো সময়টিতে করা খুব ব্যয়বহুল হয়, তাই আরও বেশি অ্যাড-হক পন্থা।

আপনি যদি হাস্যকর হতে চান, আপনি কাগজটি পুনর্ব্যবহারযোগ্য চালিয়ে যেতে পারেন যা সিপিএস এবং ডেটা হিসাবে ধারাবাহিকতার উপস্থাপনা ব্যবহার করে, তবে টেল-রিকার্সন-মডুলো-কনসের সাথে এর মতো তবে কিছু করে। এটি বর্ণনা করে যে কীভাবে আপনি রূপান্তর দ্বারা পয়েন্টার-বিপরীত অ্যালগরিদম উত্পাদন করতে পারেন।

সিপিএস রূপান্তরকরণ এবং সংশ্লেষিতকরণের এই প্যাটার্নটি বোঝার জন্য বেশ শক্তিশালী একটি সরঞ্জাম এবং আমি এখানে তালিকাভুক্ত কাগজপত্রের একটি সিরিজে ভাল প্রভাব ফেলতে ব্যবহৃত হয় ।


আপনি এখানে প্রদর্শিত ধারাবাহিকতা-পাসিং স্টাইলের জায়গায় জিসিসির যে কৌশলটি ব্যবহার করেন তা হ'ল, আমি বিশ্বাস করি, স্ট্যাটিক সিঙ্গল অ্যাসাইনমেন্ট ফর্ম।
ডেভিস্লোর

@ ডেভিস্লোর সিপিএসের সাথে সম্পর্কিত থাকাকালীন, এসএসএ কোনও পদ্ধতির নিয়ন্ত্রণ প্রবাহকে প্রভাবিত করে না এবং এটি স্ট্যাকটিকেও পরিমার্জন করে না (বা অন্যথায় ডেটা স্ট্রাকচারগুলি প্রবর্তন করে যা গতিশীলভাবে বরাদ্দ করার প্রয়োজন হবে)। এসএসএ সম্পর্কিত, সিপিএস "অত্যধিক কাজ করে" এজন্য প্রশাসনিক নর্মাল ফর্ম (এএনএফ) এসএসএর সাথে খুব বেশি ফিট। সুতরাং জিসিসি এসএসএ ব্যবহার করে, তবে এসএসএ কন্ট্রোল স্ট্যাকটিকে ম্যানিপুলেবল ডেটা স্ট্রাকচার হিসাবে দেখতে পারা যায় না।
ডেরেক এলকিনস

ঠিক। আমি এর জবাব দিচ্ছিলাম, "আমি বলছি না যে সংকলন সময়ে জিসিসি এই সমস্ত যুক্তি করে does জিসিসি কোন যুক্তি ব্যবহার করে তা আমি জানি না। " আমার উত্তর, একইভাবে, দেখানো হয়েছিল যে রূপান্তরটি তাত্ত্বিকভাবে ন্যায়সঙ্গত, এটি বলছেন না যে এটি কোনও প্রদত্ত সংকলক ব্যবহারের পদ্ধতিটি ব্যবহার করে। (যদিও আপনি জানেন, অনেক সংকলক অপ্টিমাইজেশনের সময় কোনও প্রোগ্রামকে সিপিএস-এ রূপান্তরিত করে))
ডেভিস্লোর

8

আমি কিছুক্ষণের জন্য ঝোপের চারপাশে মারতে যাচ্ছি, তবে একটি বিষয় আছে point

Semigroups

উত্তরটি হ'ল বাইনারি হ্রাস অপারেশনটির সহযোগী সম্পত্তি

এটি বেশ বিমূর্ত, তবে গুণ একটি ভাল উদাহরণ। তাহলে এক্স , Y এবং z- র কিছু স্বাভাবিক সংখ্যার (অথবা পূর্ণসংখ্যার, অথবা মূলদ সংখ্যা বা বাস্তব সংখ্যা বা জটিল সংখ্যা বা হয় এন × এন ম্যাট্রিক্স, অথবা একটি আভা কোন টি জিনিস), তারপর এক্স × Y একই ধরনের উভয় x এবং y হিসাবে সংখ্যা । আমরা দুটি সংখ্যা দিয়ে শুরু করেছি, সুতরাং এটি একটি বাইনারি অপারেশন, এবং একটি পেয়েছিলাম, তাই আমরা একের সাথে আমাদের সংখ্যার গণনা হ্রাস করেছি, এটি এটিকে হ্রাস অপারেশন করে। এবং ( x × y ) × z সর্বদা x × ( y ×) এর মতো হয় ×z ), যা সহযোগী সম্পত্তি।

(আপনি যদি ইতিমধ্যে এই সমস্ত কিছু জানেন তবে আপনি পরবর্তী বিভাগে যেতে পারেন))

কম্পিউটার বিজ্ঞানে আপনি আরও কয়েকটি জিনিস দেখতে পান যা একইভাবে কাজ করে:

  • গুণনের পরিবর্তে এই ধরণের সংখ্যার যেকোনটি যুক্ত করা
  • কানাট্যান্টিং স্ট্রিং ( "a"+"b"+"c"এটি "abc"আপনি শুরু করেন "ab"+"c"বা না "a"+"bc")
  • দুটি তালিকা একসাথে স্প্লাইক করা। [a]++[b]++[c]একইভাবে [a,b,c]হয় পিছন থেকে সামনের দিকে বা সামনে থেকে পিছনে।
  • consএকটি মাথা এবং লেজ নেভিগেশন, যদি আপনি মাথা একক তালিকা হিসাবে মনে হয়। এটি কেবল দুটি তালিকাকে সম্মতি জানানো।
  • ইউনিয়ন বা সেট ছেদ করা
  • বুলিয়ান এবং, বুলিয়ান বা
  • বিটওয়াইজ &, |এবং^
  • ফাংশনগুলির সংমিশ্রণ: ( fg ) ∘ h x = f ∘ ( gh ) x = f ( g ( h ( x )))
  • সর্বোচ্চ এবং সর্বনিম্ন
  • সংযোজন মডুলো পি

কিছু জিনিস যা না:

  • বিয়োগফল, কারণ 1- (1-2) ≠ (1-1) -2
  • xy = ট্যান ( x + y ), কারণ ট্যান (π / 4 + π / 4) অপরিবর্তিত
  • negativeণাত্মক সংখ্যার উপর গুণ কারণ, -1 × -1 একটি নেতিবাচক সংখ্যা নয়
  • তিনটি সমস্যা আছে যা পূর্ণসংখ্যার বিভাজন!
  • যৌক্তিক নয়, কারণ এটির একটি মাত্র অপারেন্ড রয়েছে, দুটি নয়
  • int print2(int x, int y) { return printf( "%d %d\n", x, y ); }হিসাবে print2( print2(x,y), z );এবং print2( x, print2(y,z) );বিভিন্ন আউটপুট আছে।

এটি একটি দরকারী যথেষ্ট ধারণা যা আমরা এটির নামকরণ করেছি। এই বৈশিষ্ট্যগুলি সহ একটি অপারেশন সহ একটি সেট হল একটি আধাগোষ্ঠী । সুতরাং, গুণনের অধীনে আসল সংখ্যাগুলি হল একটি আধাগোষ্ঠী। এবং আপনার প্রশ্নটি সত্যিকারের বিশ্বে এই ধরণের বিমূর্ততা কার্যকর হওয়ার অন্যতম উপায় হয়ে দাঁড়িয়েছে। আপনি যেভাবে জিজ্ঞাসা করছেন সেমিগ্রুপ ক্রিয়াকলাপগুলি সমস্তই অনুকূলিত হতে পারে।

বাড়িতে এটি চেষ্টা করুন

যতদূর আমি জানি, এই কৌশলটি প্রথম 1974 সালে ড্যানিয়েল ফ্রেডম্যান এবং ডেভিড ওয়াইজের গবেষণাপত্রে "ভাঁজ স্টাইলাইজড রিকার্সনগুলিকে আইট্রেসনেস" হিসাবে বর্ণনা করা হয়েছিল , যদিও তারা প্রয়োজনীয়তার চেয়ে আরও কয়েকটি সম্পত্তি ধরে নিয়েছিল

এটি চিত্রিত করার জন্য হাস্কেল একটি দুর্দান্ত ভাষা, কারণ Semigroupএটির স্ট্যান্ডার্ড লাইব্রেরিতে টাইপক্লাস রয়েছে। এটি জেনেরিক Semigroupঅপারেটরের অপারেশনকে কল করে <>। যেহেতু তালিকাগুলি এবং স্ট্রিংগুলি উদাহরণস্বরূপ Semigroup, উদাহরণস্বরূপ তাদের দৃষ্টান্তগুলি সংক্ষিপ্ত <>অপারেটর হিসাবে সংজ্ঞায়িত করা হয় ++। এবং সঠিক আমদানি সহ, [a] <> [b]একটি উপনাম [a] ++ [b], যা এটি [a,b]

তবে, সংখ্যা কী? আমরা কেবল দেখেছি যে সংখ্যার প্রকারগুলি হ'ল যোগ বা গুণফলের আওতায় সেমিগ্রুপ হয় ! যা এক হতে পায় একটি জন্য ? ভাল, হয় এক! Haskell, ধরনের সংজ্ঞায়িত , (যে Haskell, প্রকৃত সংজ্ঞা নেই), এবং এছাড়াও , ।<>DoubleProduct Doublewhere (<>) = (*)Sum Doublewhere (<>) = (+)

একটি বলি হল যে আপনি এই সত্যটি ব্যবহার করেছেন যে 1 হ'ল গুণগত পরিচয়। পরিচয়ের সাথে একটি সেমিগ্রুপকে মনোয়েড বলা হয় এবং এটি হাস্কেল প্যাকেজে সংজ্ঞায়িত হয় Data.Monoid, যা টাইপক্লাসের জেনেরিক পরিচয় উপাদান বলে memptySum, Productএবং তালিকা প্রতিটি একটি পরিচয় উপাদান (0, 1 এবং আছে []যথাক্রমে), তাই তারা উদাহরণ স্বরূপ ধরে Monoidসেইসাথে Semigroup। (একটি মোনাডের সাথে বিভ্রান্ত হওয়ার দরকার নেই , তাই ভুলে যান আমি এমনকি এগুলিও এনেছি))

মনোয়েডস ব্যবহার করে আপনার অ্যালগরিদমকে হাস্কেল ফাংশনে অনুবাদ করার জন্য এটি যথেষ্ট তথ্য:

module StylizedRec (pow) where

import Data.Monoid as DM

pow :: Monoid a => a -> Word -> a
{- Applies the monoidal operation of the type of x, whatever that is, by
 - itself n times.  This is already in Haskell as Data.Monoid.mtimes, but
 - let’s write it out as an example.
 -}
pow _ 0 = mempty -- Special case: Return the nullary product.
pow x 1 = x      -- The base case.
pow x n = x <> (pow x (n-1)) -- The recursive case.

গুরুত্বপূর্ণভাবে লক্ষ করুন যে এটি পুচ্ছ পুনরাবৃত্তি মডুলো সেমিগ্রুপ: প্রতিটি ক্ষেত্রে হয় হয় একটি মান, একটি লেজ-পুনরাবৃত্তি কল বা উভয়ের আধাগোষ্ঠী পণ্য। এছাড়াও, এই উদাহরণটি memptyএকটির ক্ষেত্রে ব্যবহার করার জন্য ঘটেছে , তবে আমাদের যদি এটির দরকার না হত, আমরা আরও সাধারণ টাইপক্লাস দিয়ে এটি করতে পারতাম Semigroup

এই প্রোগ্রামটি GHCI এ লোড করুন এবং এটি কীভাবে কাজ করে তা দেখুন:

*StylizedRec> getProduct $ pow 2 4
16
*StylizedRec> getProduct $ pow 7 2
49

মনে রাখবেন কীভাবে আমরা জেনেরিকের powজন্য ঘোষনা করেছি Monoid, যার ধরণের নাম আমরা ডাকলাম a? আমরা অনুমান করতে GHCI যথেষ্ট তথ্য দিয়েছেন যে টাইপ aএখানে Product Integer, একটি যা instanceএর Monoidযার <>অপারেশন পূর্ণসংখ্যা গুণ নয়। সুতরাং pow 2 4করতে যাও recursively বিস্তৃতি 2<>2<>2<>2, যা 2*2*2*2বা 16। এ পর্যন্ত সব ঠিকই.

তবে আমাদের ফাংশনটিতে কেবল জেনেরিক মনোয়েড অপারেশন ব্যবহার করা হয়। পূর্বে, আমি বললাম আরেক দৃষ্টান্ত নেই Monoidনামক Sum, যার <>অপারেশন +। আমরা কি তা চেষ্টা করতে পারি?

*StylizedRec> getSum $ pow 2 4
8
*StylizedRec> getSum $ pow 7 2
14

একই বিস্তৃতি এখন আমাদের 2+2+2+2পরিবর্তে দেয় 2*2*2*2। বহুগুণ সংযোজন হিসাবে এক্সপেনশনেশন হ'ল গুণ!

তবে আমি হাসকল মোনয়েডের আরও একটি উদাহরণ দিলাম: তালিকাগুলি, যার অপারেশনটি একত্রীকরণ।

*StylizedRec> pow [2] 4
[2,2,2,2]
*StylizedRec> pow [7] 2
[7,7]

লিখন সংকলককে [2]বলে যে এটি একটি তালিকা, <>তালিকায় রয়েছে ++, [2]++[2]++[2]++[2]তেমন [2,2,2,2]

অবশেষে, একটি অ্যালগরিদম (দুই, বাস্তব)

কেবল প্রতিস্থাপন xসঙ্গে [x], আপনি জেনেরিক আলগোরিদিম যে ব্যবহারসমূহ recursion মডিউল একটি semigroup এক যে একটি তালিকা তৈরি করে পরিবর্তিত করে। কোন তালিকা? অ্যালগোরিদম উপাদানগুলির তালিকার জন্য প্রযোজ্য <> যেহেতু আমরা কেবলমাত্র সেমিগ্রুপ ক্রিয়াকলাপগুলি ব্যবহার করেছি যা তালিকাগুলি খুব বেশি রয়েছে, ফলে ফলাফলটি মূল গণনার ক্ষেত্রে বিচ্ছিন্ন হবে। এবং যেহেতু মূল অপারেশনটি সহকারী ছিল, তাই আমরা পিছন থেকে সামনের দিক থেকে বা সামনে থেকে উপাদানগুলি সমানভাবে ভালভাবে মূল্যায়ন করতে পারি।

যদি আপনার অ্যালগরিদম কখনও কোনও বেস কেসে পৌঁছায় এবং শেষ হয় তবে তালিকাটি খালি থাকবে না। যেহেতু টার্মিনাল কেসটি কিছু ফিরিয়ে দিয়েছে, এটি তালিকার চূড়ান্ত উপাদান হবে, সুতরাং এতে কমপক্ষে একটি উপাদান থাকবে।

আপনি যাতে তালিকার প্রতিটি উপাদানগুলিতে বাইনারি হ্রাস অপারেশনটি কীভাবে প্রয়োগ করেন? একদম ঠিক আছে। সুতরাং আপনি প্রতিস্থাপন করতে পারেন [x]জন্য xদ্বারা কমাতে উপাদানের একটি তালিকা পেতে <>, এবং তারপর পারেন ডান-ভাঁজ বা তালিকা বাম-ভাঁজ:

*StylizedRec> getProduct $ foldr1 (<>) $ pow [Product 2] 4
16
*StylizedRec> import Data.List
*StylizedRec Data.List> getProduct $ foldl1' (<>) $ pow [Product 2] 4
16

সঙ্গে সংস্করণ foldr1আসলে মান গ্রন্থাগার বিদ্যমান, যেমন sconcatজন্য Semigroupএবং mconcatজন্য Monoid। এটি তালিকায় একটি অলস ডান ভাঁজ করে। অর্থাৎ এটি প্রসারিত [Product 2,Product 2,Product 2,Product 2]হয় 2<>(2<>(2<>(2)))

এটি এই ক্ষেত্রে দক্ষ নয় কারণ আপনি সমস্তটি তৈরি না করা পর্যন্ত আপনি স্বতন্ত্র পদগুলির সাথে কিছুই করতে পারবেন না। (এক পর্যায়ে আমার ডান ভাঁজগুলি কখন ব্যবহার করতে হবে এবং কখন কঠোর বাম ভাঁজগুলি ব্যবহার করা উচিত সে সম্পর্কে আমি এখানে আলোচনা করেছি, তবে এটি খুব বেশি দূরে চলে গেছে))

সহ সংস্করণটি foldl1'কঠোরভাবে মূল্যায়ন করা বাম ভাঁজ। এটি বলতে হয়, একটি কঠোর সঞ্চয়ের সাথে একটি পুচ্ছ-পুনরাবৃত্তি ফাংশন। এটি মূল্যায়ন করে (((2)<>2)<>2)<>2, তাৎক্ষণিকভাবে গণনা করা হয় এবং পরে যখন প্রয়োজন হয় না। (অন্তত, সেখানে মধ্যে কোন বিলম্ব হয় নিজেই ভাঁজ। তালিকা গুটান হচ্ছে আরেকটি ফাংশন যা অলস মূল্যায়ন থাকতে পারে এখানে উৎপন্ন হয়) সুতরাং, ভাঁজ গণনা করে (4<>2)<>2, তারপর অবিলম্বে গণনা করে 8<>2, তারপর 16। এই কারণেই আমাদের সহযোগী হওয়ার জন্য অপারেশনটি প্রয়োজন: আমরা সবেমাত্র বন্ধনীগুলির গোষ্ঠী পরিবর্তন করেছি!

কঠোর বাম ভাঁজটি জিসিসি যা করছে তার সমতুল্য। পূর্ববর্তী উদাহরণের বামতম সংখ্যাটি হ'ল সঞ্চালক, এক্ষেত্রে একটি চলমান পণ্য। প্রতিটি পদক্ষেপে, এটি তালিকার পরবর্তী সংখ্যা দ্বারা গুণিত হয়। এটি প্রকাশের আর একটি উপায় যা হ'ল: আপনি গুণমানের মানগুলি নিয়ে পুনরাবৃত্তি করেন, চলমান পণ্যটিকে একটি সঞ্চয়ে রেখে রাখেন এবং প্রতিটি পুনরাবৃত্তির উপর, আপনি পরবর্তী মান দ্বারা সংযোজককে গুণ করেন। যে, এটি whileছদ্মবেশ একটি লুপ।

এটি কখনও কখনও ঠিক দক্ষ হিসাবে তৈরি করা যেতে পারে। সংকলক মেমরিতে তালিকার ডেটা কাঠামোটিকে অপ্টিমাইজ করতে সক্ষম হতে পারে। তত্ত্ব অনুসারে, এখানে এটি করা উচিত তা নির্ধারণের জন্য এটির সংকলনের সময় পর্যাপ্ত তথ্য রয়েছে: [x]এটি একটি সিঙ্গলটন, [x]<>xsএকইরকম cons x xs। ফাংশনটির প্রতিটি পুনরাবৃত্তি একই স্ট্যাক ফ্রেমটিকে পুনরায় ব্যবহার করতে এবং প্যারামিটারগুলিকে আপডেট করতে সক্ষম হতে পারে।

একটি ডান ভাঁজ বা একটি কঠোর বাম ভাঁজ একটি উপযুক্ত ক্ষেত্রে আরও উপযুক্ত হতে পারে, তাই আপনি কোনটি চান তা জেনে রাখুন। এমন কিছু জিনিস রয়েছে যা কেবলমাত্র একটি ডান ভাঁজ করতে পারে (যেমন সমস্ত ইনপুটটির জন্য অপেক্ষা না করে ইন্টারেক্টিভ আউটপুট উত্পন্ন করে এবং একটি অসীম তালিকায় কাজ করে)। এখানে, যদিও আমরা অপারেশনগুলির ক্রমকে একটি সাধারণ মানকে হ্রাস করছি, তাই কঠোর বাম ভাঁজটি আমরা চাই।

সুতরাং, যেমন আপনি দেখতে পাচ্ছেন, লেজ-পুনরাবৃত্তি মডুলোর যে কোনও সেমিগ্রুপকে স্বয়ংক্রিয়ভাবে অনুকূলকরণ করা সম্ভব (এর একটি উদাহরণ উদাহরণের মধ্যে যে কোনও সংখ্যাবৃদ্ধির অধীনে রয়েছে) একটি অলস ডান ভাঁজ বা একটি শক্ত বাম ভাঁজ, এক লাইনের এক লাইনে Haskell,।

আরও সাধারণীকরণ

বাইনারি অপারেশনের দুটি আর্গুমেন্ট একই ধরণের হওয়া উচিত নয়, যতক্ষণ না আপনার মান হিসাবে প্রাথমিক মান একই ধরণের হয়। (আপনি অবশ্যই বাম বা ডান যে ধরণের ভাঁজ করছেন তার ক্রমের সাথে মেলে ধরার জন্য আপনি সর্বদা আর্গুমেন্টগুলি ফ্লিপ করতে পারেন)) সুতরাং আপনি বারবার কোনও ফাইল আপডেট করার জন্য একটি প্যাচ যুক্ত করতে পারেন, বা এর প্রাথমিক মান দিয়ে শুরু করতে পারেন 1.0, একটি ভাসমান-পয়েন্ট ফলাফল সংগ্রহ করতে পূর্ণসংখ্যা দ্বারা ভাগ করুন। অথবা একটি তালিকা পেতে খালি তালিকায় উপাদানগুলি প্রিপেন্ড করুন।

আর এক ধরণের সাধারণীকরণ হ'ল ভাঁজগুলি তালিকাগুলিতে নয় বরং অন্যান্য Foldableডেটা স্ট্রাকচারে প্রয়োগ করা । প্রায়শই, অপরিবর্তনীয় রৈখিক লিঙ্কযুক্ত তালিকাটি কোনও প্রদত্ত অ্যালগরিদমের জন্য আপনি যে ডেটা কাঠামো চান তা নয়। একটি বিষয় যা আমি উপরে উঠতে পারি নি তা হ'ল পিছনের তুলনায় তালিকার সামনের অংশে উপাদান যুক্ত করা অনেক বেশি দক্ষ এবং যখন অপারেশনটি পরিবর্তনীয় হয় না তখন অপারেশনটির xবামে এবং ডানদিকে প্রয়োগ হয় না একই. সুতরাং আপনাকে অন্য কাঠামো ব্যবহার করতে হবে, যেমন তালিকার জুড়ি বা বাইনারি গাছের মতো, এমন একটি অ্যালগোরিদম উপস্থাপন করতে xযা ডানদিকে <>এবং বামে প্রয়োগ করতে পারে ।

আরও মনে রাখবেন যে সহযোগী সম্পত্তি আপনাকে বিভাজন এবং বিজয়ের মতো অন্যান্য দরকারী উপায়ে ক্রিয়াকলাপগুলি পুনরায় গোষ্ঠীভুক্ত করতে দেয়:

times :: Monoid a => a -> Word -> a
times _ 0 = mempty
times x 1 = x
times x n | even n    = y <> y
          | otherwise = x <> y <> y
  where y = times x (n `quot` 2)

বা স্বয়ংক্রিয় সমান্তরালতা, যেখানে প্রতিটি থ্রেড অন্যের সাথে মিলিত হয়ে এমন কোনও মানকে একটি সাব্রিন্স হ্রাস করে।


1
আমরা পরীক্ষা করার জন্য একটি পরীক্ষা করতে পারি যে সাহিত্যতা এই অপ্টিমাইজেশানটি করার জন্য জিসিসির দক্ষতার মূল চাবিকাঠি: একটি pow(float x, unsigned n)সংস্করণ gcc.godbolt.org/z/eqwine কেবলমাত্র এর সাথে অনুকূল হয় -ffast-math, (যা বোঝায় St-fassociative-math কঠোর ভাসমান বিন্দু অবশ্যই সংঘবদ্ধ নয় কারণ বিভিন্ন টেম্পোররিজ = বিভিন্ন বৃত্তাকার)। এমন একটি পরিচিতি দেয় 1.0f * xযা সি অ্যাবস্ট্রাক্ট মেশিনে উপস্থিত ছিল না (তবে যা সর্বদা একটি অভিন্ন ফলাফল দেবে)। তারপরে এন -1 এর গুণগুলি do{res*=x;}while(--n!=1)পুনরাবৃত্তির সমান, সুতরাং এটি একটি মিস অপটিমাইজেশন।
পিটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.