হ্রাস, ভাঁজ বা স্ক্যান (বাম / ডান)?


186

যখন আমি ব্যবহার করা উচিত reduceLeft, reduceRight, foldLeft, foldRight, scanLeftবা scanRight?

আমি তাদের পার্থক্যের একটি অন্তর্দৃষ্টি / ওভারভিউ চাই - সম্ভবত কিছু সহজ উদাহরণ সহ।


সুপারিশ আপনি দেখতে stackoverflow.com/questions/25158780/...
samthebest

1
পয়েন্টারের জন্য ধন্যবাদ। এটি আমার প্রযুক্তিগত জ্ঞানের তুলনায় কিছুটা উপরে :) আমার উত্তরে এমন কিছু আছে যা আপনার মনে হয় স্পষ্ট করা / পরিবর্তন করা উচিত?
মার্ক গ্রু

না, কেবল ইতিহাসের কিছুটা অংশ এবং এমপিপির সাথে প্রাসঙ্গিকতার দিকে নির্দেশ করা।
সামথিবেষ্ট

ওয়েল, কঠোরভাবে মধ্যে পার্থক্য ভাষী reduceএবং foldএকটি শুরুর মান অস্তিত্ব নয় - বরং একটি হল ফল আরো একটি গভীর অন্তর্নিহিত গাণিতিক যুক্তির।
সামথিবেষ্ট

উত্তর:


370

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

reduceLeftএবং reduceRightএকক ফলাফল সংগ্রহ করা।

foldLeftএবং foldRightএকটি শুরুর মানটি ব্যবহার করে একটি ফলাফল অর্জন করুন।

scanLeftএবং scanRightএকটি প্রারম্ভিক মান ব্যবহার করে মধ্যবর্তী संचयी ফলাফলগুলির সংগ্রহ সংগ্রহ করুন ulate

স্তূপাকার করা

বাম এবং এগিয়ে থেকে ...

উপাদানগুলির সংগ্রহ abcএবং একটি বাইনারি অপারেটরের সাহায্যে addআমরা সংগ্রহের এলএইফটি উপাদানটি থেকে এগিয়ে যাওয়ার সময় বিভিন্ন ভাঁজ ফাংশনগুলি কী করে তা অন্বেষণ করতে পারি (এ থেকে সিতে):

val abc = List("A", "B", "C")

def add(res: String, x: String) = { 
  println(s"op: $res + $x = ${res + x}")
  res + x
}

abc.reduceLeft(add)
// op: A + B = AB
// op: AB + C = ABC    // accumulates value AB in *first* operator arg `res`
// res: String = ABC

abc.foldLeft("z")(add) // with start value "z"
// op: z + A = zA      // initial extra operation
// op: zA + B = zAB
// op: zAB + C = zABC
// res: String = zABC

abc.scanLeft("z")(add)
// op: z + A = zA      // same operations as foldLeft above...
// op: zA + B = zAB
// op: zAB + C = zABC
// res: List[String] = List(z, zA, zAB, zABC) // maps intermediate results


ডান দিক থেকে এবং পিছনে ...

আমরা যদি সঠিক উপাদানটি দিয়ে শুরু করি এবং পিছনের দিকে চলে যাই (সি থেকে ক এ) আমরা লক্ষ্য করব যে এখন আমাদের বাইনারি অপারেটরের দ্বিতীয় তর্কটি ফলাফল সংগ্রহ করে (অপারেটরটি একই, আমরা তাদের ভূমিকাগুলি পরিষ্কার করার জন্য আর্গুমেন্টের নামগুলি পরিবর্তন করেছি) ):

def add(x: String, res: String) = {
  println(s"op: $x + $res = ${x + res}")
  x + res
}

abc.reduceRight(add)
// op: B + C = BC
// op: A + BC = ABC  // accumulates value BC in *second* operator arg `res`
// res: String = ABC

abc.foldRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: String = ABCz

abc.scanRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: List[String] = List(ABCz, BCz, Cz, z)

ডি-স্তূপীকৃত করা

বাম এবং এগিয়ে থেকে ...

পরিবর্তে যদি আমরা কোনও সংগ্রহের এলএইফটি উপাদান থেকে শুরু করে বিয়োগ করে কিছু ফলাফল ডি-কমুলেট করি, আমরা resআমাদের বাইনারি অপারেটরের প্রথম যুক্তির মাধ্যমে ফলাফলটি সংহত করব minus:

val xs = List(1, 2, 3, 4)

def minus(res: Int, x: Int) = {
  println(s"op: $res - $x = ${res - x}")
  res - x
}

xs.reduceLeft(minus)
// op: 1 - 2 = -1
// op: -1 - 3 = -4  // de-cumulates value -1 in *first* operator arg `res`
// op: -4 - 4 = -8
// res: Int = -8

xs.foldLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: Int = -10

xs.scanLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: List[Int] = List(0, -1, -3, -6, -10)


ডান দিক থেকে এবং পিছনে ...

তবে এখনই এক্সরাইটের বৈচিত্রগুলি সন্ধান করুন! মনে রাখবেন যে xRight প্রকরণের (ডি-) জমে থাকা মানটি আমাদের বাইনারি অপারেটরের দ্বিতীয় প্যারামিটারে চলে গেছে :resminus

def minus(x: Int, res: Int) = {
  println(s"op: $x - $res = ${x - res}")
  x - res
}

xs.reduceRight(minus)
// op: 3 - 4 = -1
// op: 2 - -1 = 3  // de-cumulates value -1 in *second* operator arg `res`
// op: 1 - 3 = -2
// res: Int = -2

xs.foldRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: Int = -2

xs.scanRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: List[Int] = List(-2, 3, -1, 4, 0) 

শেষ তালিকা (-2, 3, -1, 4, 0) সম্ভবত আপনি যা স্বজ্ঞাতভাবে আশা করবেন তা নয়!

আপনি দেখতে পাচ্ছেন, পরিবর্তে কেবল একটি স্ক্যানএক্স চালিয়ে আপনার ফোল্ডএক্স কী করছে তা পরীক্ষা করতে পারেন এবং প্রতিটি পদক্ষেপে বদ্ধ ফলাফলটি ডিবাগ করতে পারেন।

শেষের সারি

  • reduceLeftবা এর সাথে একটি ফলাফল কমুলেট করুন reduceRight
  • সঙ্গে একটি ফলাফলের স্তূপীকৃত করা foldLeftবা foldRightআপনি একটি সূচনা মান রয়েছে।
  • scanLeftবা এর সাথে মধ্যবর্তী ফলাফলের সংকলন সহ্য করুন scanRight

  • আপনি যদি সংগ্রহের মাধ্যমে এগিয়ে যেতে চান তবে একটি এক্স লেফট প্রকরণটি ব্যবহার করুন ।

  • আপনি যদি সংগ্রহের মাধ্যমে পিছনের দিকে যেতে চান তবে এক্সরেট প্রকরণটি ব্যবহার করুন ।

14
যদি আমার ভুল না হয় তবে বাম সংস্করণটি টেল কল অপ্টিমাইজেশন ব্যবহার করতে পারে, যার অর্থ এটি অনেক বেশি দক্ষ efficient
ট্রিলিক্স

3
@ মার্ক, আমি চিঠিগুলির উদাহরণগুলি পছন্দ করি, এটি বিষয়গুলি খুব স্পষ্ট করে তুলেছে
মুহাম্মদ ফারাগ

@ ট্রিলিক্স ফোল্ড রাইট টেলিমেক দিয়ে প্রয়োগ করা যেতে পারে
তীমথিয় কিম

@ টিমোথিমকিম এটি করতে অপ্টিমাইজড অ-সোজা বাস্তবায়নগুলি সহ এটি করতে পারে। উদাহরণস্বরূপ স্কালাল তালিকার বিশেষ ক্ষেত্রে , সেই পদ্ধতিটি Listতারপরে প্রয়োগের ক্ষেত্রে বিপরীতকরণের উপর নির্ভর করে foldLeft। অন্যান্য সংগ্রহগুলি বিভিন্ন কৌশল প্রয়োগ করতে পারে। সাধারণভাবে, যদি foldLeftএবং foldRightবিনিময়যোগ্যভাবে ব্যবহার করা যায় (প্রয়োগ করা অপারেটরের সহযোগী সম্পত্তি), তবে foldLeftএটি আরও দক্ষ এবং পছন্দসই।
ট্রিলস

9

সাধারণত রেডু, ফোল্ড, স্ক্যান পদ্ধতি বামে ডেটা সংগ্রহ করে এবং রাইট ভেরিয়েবল পরিবর্তন করে চলে। তাদের মধ্যে প্রধান পার্থক্য হ্রাস করা হয়, ভাঁজ হয়: -

ভাঁজ সর্বদা একটি seedমান অর্থাত্ ব্যবহারকারী সংজ্ঞায়িত শুরু মান দিয়ে শুরু হবে। ভাঁজ বীজের মান ফিরিয়ে দেয় যেখানে সংগ্রহ খালি থাকলে হ্রাস একটি ব্যতিক্রম ছুঁড়ে ফেলবে।সর্বদা একটি একক মান ফলাফল।

বাম বা ডান হাত থেকে আইটেমগুলির কিছু প্রক্রিয়াকরণ ক্রমের জন্য স্ক্যান ব্যবহার করা হয়, তারপরে আমরা পরবর্তী ফলাফলগুলি পরবর্তী গণনার ক্ষেত্রে ব্যবহার করতে পারি। তার মানে আমরা আইটেমগুলি স্ক্যান করতে পারি। সর্বদা একটি সংগ্রহ ফলাফল।

  • LEFT_REDUCE পদ্ধতি REDUCE পদ্ধতির অনুরূপ কাজ করে।
  • বাম দিকটি হ্রাস করার বিপরীতে RIGHT_REDUCE অর্থাৎ এটি RIGHT এ মান সংগ্রহ করে এবং বাম ভেরিয়েবল পরিবর্তন করে চলেছে changing

  • কমান্ড লেফটপশন এবং কমাইরাইট অপশনটি বাম_আরডাসের সাথে সমান এবং ডান_ড্রেস পার্থক্য কেবলমাত্র তারা বিকল্প বিকল্পটিতে ফলাফল ফেরত দেয়।

নীচে উল্লিখিত কোডের আউটপুটগুলির একটি অংশটি হ'ল: -

scanসংখ্যার তালিকার উপর অপারেশন ব্যবহার করে ( seedমান ব্যবহার করে 0)List(-2,-1,0,1,2)

  • {0, -2} => - 2 {-2, -1} => - 3 {-3,0} => - 3 {-3,1} => - 2 {-2,2} => 0 স্ক্যান তালিকা (0, -2, -3, -3, -2, 0)

  • {0, -2} => - 2 {-2, -1} => - 3 {-3,0} => - 3 {-3,1} => - 2 {-2,2} => 0 স্ক্যান লেফট (এ + বি) তালিকা (0, -2, -3, -3, -2, 0)

  • {0, -2} => - 2 {-2, -1} => - 3 {-3,0} => - 3 {-3,1} => - 2 {-2,2} => 0 স্ক্যান লেফট (বি + এ) তালিকা (0, -2, -3, -3, -2, 0)

  • {2,0} => 2 {1,2} => 3 {0,3} => 3 {-1,3} => 2 {-2,2} => 0 স্ক্যানরাইট (এ + বি) তালিকা ( 0, 2, 3, 3, 2, 0)

  • {2,0} => 2 {1,2} => 3 {0,3} => 3 {-1,3} => 2 {-2,2} => 0 স্ক্যানরাইট (বি + এ) তালিকা ( 0, 2, 3, 3, 2, 0)

ব্যবহার reduce, foldস্ট্রিং একটি তালিকা ধরে অভিযানList("A","B","C","D","E")

  • {A, B} => AB {AB, C} => ABC {ABC, D} => ABCD {ABCD, E} => ABCDE হ্রাস করুন (a + b) ABCDE
  • {এ, বি} => এবি {এ বি, সি} => এ বি সি {এ বি সি, ডি BC => এ বি সি ডি {এ বি সি ডি, ই reduce => এ বি সি ডি ডি কমিয়ে বাম (ক + খ) এ বি সি ডি ডি
  • {এ, বি} => বিএ {বিএ, সি BA => সিবিএ {সিবিএ, ডি} => ডিসিবিএ {ডিসিবিএ, ই} => ইডিসিবিএ হ্রাস লেফট (বি + এ) ইডিসিবি
  • {ডি, ই} => ডি {সি, ডিই} => সিডিই {বি, সিডিই} => বিসিডিই {এ, বিসিডিই A => এবিসিডিই হ্রাস রাইট (এ + বি) এবিসিডিই
  • {ডি, ই} => ইডি {সি, ইডি} => ইডিসি {বি, ইডিসি} => ইডিসিবি {এ, ইডিসিবি} => ইডিসিবিএ হ্রাস রাইট (বি + এ) ইডিসিবিএ

কোড:

object ScanFoldReduce extends App {

    val list = List("A","B","C","D","E")
            println("reduce (a+b) "+list.reduce((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (a+b) "+list.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (b+a) "+list.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))

            println("reduceRight (a+b) "+list.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("reduceRight (b+a) "+list.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  ")
                b+a
            }))

            println("scan            "+list.scan("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))
            println("scanLeft (a+b)  "+list.scanLeft("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))
            println("scanLeft (b+a)  "+list.scanLeft("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))
            println("scanRight (a+b) "+list.scanRight("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))
            println("scanRight (b+a) "+list.scanRight("[")((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))
//Using numbers
     val list1 = List(-2,-1,0,1,2)

            println("reduce (a+b) "+list1.reduce((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (a+b) "+list1.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  ")
                a+b
            }))

            println("reduceLeft (b+a) "+list1.reduceLeft((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))

            println("      reduceRight (a+b) "+list1.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("      reduceRight (b+a) "+list1.reduceRight((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  ")
                b+a
            }))

            println("scan            "+list1.scan(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("scanLeft (a+b)  "+list1.scanLeft(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b
            }))

            println("scanLeft (b+a)  "+list1.scanLeft(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (b+a)+"  " )
                b+a
            }))

            println("scanRight (a+b)         "+list1.scanRight(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                a+b}))

            println("scanRight (b+a)         "+list1.scanRight(0)((a,b)=>{
                print("{"+a+","+b+"}=>"+ (a+b)+"  " )
                b+a}))
}

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

4

সংগ্রহের জন্য x উপাদানসমূহ x0, x1, x2, x3 এবং একটি স্বেচ্ছাসেবী ফাংশন আপনার সাথে নিম্নলিখিতগুলি রয়েছে:

1. x.reduceLeft    (f) is f(f(f(x0,x1),x2),x3) - notice 3 function calls
2. x.reduceRight   (f) is f(f(f(x3,x2),x1),x0) - notice 3 function calls
3. x.foldLeft (init,f) is f(f(f(f(init,x0),x1),x2),x3) - notice 4 function calls
4. x.foldRight(init,f) is f(f(f(f(init,x3),x2),x1),x0) - notice 4 function calls
5. x.scanLeft (init,f) is f(init,x0)=g0
                          f(f(init,x0),x1) = f(g0,x1) = g1
                          f(f(f(init,x0),x1),x2) = f(g1,x2) = g2
                          f(f(f(f(init,x0),x1),x2),x3) = f(g2,x3) = g3
                          - notice 4 function calls but also 4 emitted values
                          - last element is identical with foldLeft
6. x.scanRight (init,f) is f(init,x3)=h0
                          f(f(init,x3),x2) = f(h0,x2) = h1
                          f(f(f(init,x3),x2),x1) = f(h1,x1) = h2
                          f(f(f(f(init,x3),x2),x1),x0) = f(h2,x0) = h3
                          - notice 4 function calls but also 4 emitted values
                          - last element is identical with foldRight

উপসংহারে

  • scanএর মতো foldতবে সমস্ত মধ্যবর্তী মানগুলিও প্রকাশ করে
  • reduce প্রাথমিক মানটির প্রয়োজন হয় না যা কখনও কখনও খুঁজে পাওয়া একটু কঠিন
  • fold একটি প্রাথমিক মান প্রয়োজন যা খুঁজে পাওয়া একটু শক্ত is
    • অঙ্কের জন্য 0
    • পণ্যের জন্য 1
    • মিনিটের জন্য প্রথম উপাদান (কেউ কেউ পূর্ণসংখ্যার প্রস্তাব দিতে পারে MA MAX_VALUE)
  • 100% নিশ্চিত নয় তবে দেখে মনে হচ্ছে এখানে এই সমতুল্য বাস্তবায়ন রয়েছে:
    • x.reduceLeft(f) === x.drop(1).foldLeft(x.head,f)
    • x.foldRight(init,f) === x.reverse.foldLeft(init,f)
    • x.foldLeft(init,f) === x.scanLeft(init,f).last
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.