একটি ভাঁজ তাড়াতাড়ি বাতিল


90

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

def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
  nums.foldLeft (Some(0): Option[Int]) {
    case (Some(s), n) if n % 2 == 0 => Some(s + n)
    case _ => None
  }
}

যাইহোক, এই সমাধানটি বেশ কুৎসিত (যেমন, যদি আমি একটি। ফরাসী এবং একটি রিটার্ন করতাম - তবে এটি আরও পরিস্কার এবং পরিষ্কার হবে) এবং সর্বোপরি সবচেয়ে খারাপ, এটি যদি কোনও অ-সম সংখ্যার মুখোমুখি হয় তবেও এটি পুরো পুনরাবৃত্তিকে অনুসরণ করে es ।

তাহলে এর আগে কোনও ফোল্ড লেখার সর্বোত্তম উপায় কী হবে, তা তাড়াতাড়ি বন্ধ হয়ে যায়? আমি কি কেবল এই পুনরাবৃত্তভাবে লিখতে গিয়ে লিখতে পারি, না আরও কোনও গ্রহণযোগ্য উপায় আছে?


আপনি কি মধ্যবর্তী উত্তরটি শেষ করে এবং রেকর্ড করতে চান?
ব্রায়ান অগ্নিউ

এই ক্ষেত্রে, না। তবে কিছুটা সাধারণ ক্ষেত্রে আমি যে কোনও
একটিতে

: এই প্রশ্ন হল stackoverflow.com/questions/1595427/...
ziggystar

: লুপ থেকে বের ভঙ্গ সম্পর্কে এই উত্তর আরও দেখা যেতে পারে দরকারী stackoverflow.com/a/2742941/1307721
ejoubaud

উত্তর:


65

আমার প্রথম পছন্দটি সাধারণত পুনরাবৃত্তি ব্যবহার করা হবে। এটি কেবলমাত্র পরিমিতভাবে কম কমপ্যাক্ট, সম্ভাব্যতর দ্রুত (অবশ্যই ধীর নয়) এবং প্রারম্ভিক সমাপ্তির পরে যুক্তি আরও সুস্পষ্ট করে তুলতে পারে। এক্ষেত্রে আপনার নেস্টেড ডিফস দরকার যা কিছুটা বিশ্রী:

def sumEvenNumbers(nums: Iterable[Int]) = {
  def sumEven(it: Iterator[Int], n: Int): Option[Int] = {
    if (it.hasNext) {
      val x = it.next
      if ((x % 2) == 0) sumEven(it, n+x) else None
    }
    else Some(n)
  }
  sumEven(nums.iterator, 0)
}

আমার দ্বিতীয় পছন্দটি ব্যবহার করা হবে return, যেহেতু এটি অন্য সমস্ত কিছু অক্ষত রাখে এবং আপনাকে কেবল সেই ভাঁজটি মোড়ানো প্রয়োজন defযাতে আপনার কিছু থেকে ফিরে আসতে পারে - এই ক্ষেত্রে আপনার ইতিমধ্যে একটি পদ্ধতি রয়েছে, তাই:

def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
  Some(nums.foldLeft(0){ (n,x) =>
    if ((n % 2) != 0) return None
    n+x
  })
}

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

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

import scala.util.control.ControlThrowable
case class Returned[A](value: A) extends ControlThrowable {}
def shortcut[A](a: => A) = try { a } catch { case Returned(v) => v }

def sumEvenNumbers(nums: Iterable[Int]) = shortcut{
  Option(nums.foldLeft(0){ (n,x) =>
    if ((x % 2) != 0) throw Returned(None)
    n+x
  })
}

এখানে অবশ্যই ব্যবহার returnকরা ভাল, তবে মনে রাখবেন যে আপনি যে shortcutকোনও জায়গায় পুরোপুরি গুছিয়ে রাখতে পারেন না, যে কোনও জায়গায় রাখতে পারেন ।

আমার জন্য পরবর্তী লাইনটি ভাঁজগুলি পুনরায় বাস্তবায়ন করা হবে (হয় নিজেই বা এটির একটি লাইব্রেরি সন্ধান করা) যাতে এটি প্রারম্ভিক সমাপ্তির ইঙ্গিত দিতে পারে। এটি করার দুটি প্রাকৃতিক উপায় হ'ল মূল্য প্রচার করা নয় বরং একটি Optionমান যুক্ত রয়েছে যেখানে Noneসমাপ্তির ইঙ্গিত দেয়; বা দ্বিতীয় সূচক ফাংশন ব্যবহার করতে যা সম্পূর্ণরূপে সংকেত দেয়। কিম স্টেবেলের দেখানো স্ক্যালাজ আলস্য ভাঁজ ইতিমধ্যে প্রথম কেসটি কভার করে, তাই আমি দ্বিতীয়টি দেখাব (একটি পরিবর্তনীয় বাস্তবায়ন সহ):

def foldOrFail[A,B](it: Iterable[A])(zero: B)(fail: A => Boolean)(f: (B,A) => B): Option[B] = {
  val ii = it.iterator
  var b = zero
  while (ii.hasNext) {
    val x = ii.next
    if (fail(x)) return None
    b = f(b,x)
  }
  Some(b)
}

def sumEvenNumbers(nums: Iterable[Int]) = foldOrFail(nums)(0)(_ % 2 != 0)(_ + _)

(আপনি পুনরাবৃত্তি, রিটার্ন, অলসতা ইত্যাদির মাধ্যমে সমাপ্তি বাস্তবায়ন করেন কিনা তা আপনার উপর নির্ভর করে))

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


foldOrFailঠিক কি আমি প্রশ্ন সম্পর্কে চিন্তা সঙ্গে আসা পর্যন্ত ছিল না। কোনও সুন্দরভাবে আবদ্ধ হয়ে গেলে বাস্তবায়ন আইএমওতে কোনও পরিবর্তনীয় পুনরুক্তি এবং কিছুক্ষণ লুপ ব্যবহার না করার কারণ নেই। iteratorপুনরাবৃত্তির সাথে ব্যবহার করার অর্থ হয় না।
0__

@ রেক্স কের, আপনার জবাবের জন্য ধন্যবাদ আমি নিজের ব্যবহারের জন্য এমন একটি সংস্করণ টিকিয়েছি যা এর আগে ব্যবহার হয় ... (আমি এটি উত্তর হিসাবে পোস্ট করব)
কোর

প্রত্যাবর্তন- ভিত্তিক সমাধানের পক্ষে একটি সম্ভবত , এটি কোন ফাংশনটি প্রয়োগ করে তা বুঝতে সময় লাগবে: sumEvenNumbersবা ভাঁজ হয়েছেop
ইভান বালশভ

4
@ ইভানবালাভ - ঠিক আছে, স্কালার বিধিগুলি কী জন্য তা শিখতে একবার সময় লাগবে return(অর্থাত এটি আপনাকে যে অভ্যন্তরীণ স্পষ্ট পদ্ধতিতে পেয়েছে তা থেকে ফিরে আসে) তবে এর পরে খুব বেশি সময় নেওয়া উচিত নয়। নিয়মটি বেশ পরিষ্কার, এবং defযেখানে ঘের লাগানোর পদ্ধতিটি তা দেয়।
রেক্স কের

4
আমি তোমার foldOrFail মত কিন্তু ব্যক্তিগতভাবে আমি রিটার্ন টাইপ করে দিতেন Bনা Option[B]কারণ তাহলে এটি ভাঁজ মত আচরণ যেখানে রিটার্ন টাইপ শূন্য সঁচায়ক ধরন একই। খালি খালি সমস্ত অপশন রিটার্ন প্রতিস্থাপন করুন। এবং শূন্য হিসাবে কোনটিতে পাস করুন। সবকিছুর পরেও এমন একটি ভাঁজ চেয়েছিল যা ব্যর্থ হওয়ার চেয়ে তাড়াতাড়ি শেষ হতে পারে।
কার্ল

26

আপনি যে দৃশ্যের বর্ণনা দিয়েছেন (কিছু অযাচিত অবস্থার উপর দিয়ে প্রস্থান করুন) takeWhileপদ্ধতিটির জন্য ভাল ব্যবহারের ক্ষেত্রে মনে হচ্ছে । এটি মূলত filter, তবে শর্তটি পূরণ করে না এমন উপাদানটির মুখোমুখি হওয়া উচিত।

উদাহরণ স্বরূপ:

val list = List(2,4,6,8,6,4,2,5,3,2)
list.takeWhile(_ % 2 == 0) //result is List(2,4,6,8,6,4,2)

এটি Iterators / Iterables এর জন্যও ঠিক কাজ করবে । আপনার "সমান সংখ্যার যোগফল, তবে বিজোড় বিভাজন" এর জন্য আমি যে সমাধানটি প্রস্তাব করি তা হ'ল:

list.iterator.takeWhile(_ % 2 == 0).foldLeft(...)

এবং কেবলমাত্র এটি প্রমাণ করার জন্য যে এটি একবার আপনার বিজোড় সংখ্যায় হিট করে আপনার সময় নষ্ট করছে না ...

scala> val list = List(2,4,5,6,8)
list: List[Int] = List(2, 4, 5, 6, 8)

scala> def condition(i: Int) = {
     |   println("processing " + i)
     |   i % 2 == 0
     | }
condition: (i: Int)Boolean

scala> list.iterator.takeWhile(condition _).sum
processing 2
processing 4
processing 5
res4: Int = 6

এটি ঠিক সেই ধরণের সরলতার জন্য আমি খুঁজছিলাম - ধন্যবাদ!
ট্যানার

14

স্ক্যালাজে ফোল্ডরাইটের অলস সংস্করণ ব্যবহার করে আপনি যা কার্যকরী শৈলীতে চান তা করতে পারেন। আরও গভীরতার ব্যাখ্যার জন্য, এই ব্লগ পোস্টটি দেখুন । এই সমাধান একটি ব্যবহার করার সময় Stream, আপনি একটি রূপান্তর করতে পারেন Iterableএকটি মধ্যে Streamদক্ষতার সঙ্গে iterable.toStream

import scalaz._
import Scalaz._

val str = Stream(2,1,2,2,2,2,2,2,2)
var i = 0 //only here for testing
val r = str.foldr(Some(0):Option[Int])((n,s) => {
  println(i)
  i+=1
  if (n % 2 == 0) s.map(n+) else None
})

এটি কেবল প্রিন্ট করে

0
1

যা পরিষ্কারভাবে দেখায় যে বেনামে ফাংশনটি কেবলমাত্র দু'বার বলা হয় (অর্থাত্ এটি বিজোড় সংখ্যার মুখোমুখি হওয়া অবধি)। এটি ফোল্ডারের সংজ্ঞার কারণে, যার স্বাক্ষর (ক্ষেত্রে Stream) def foldr[B](b: B)(f: (Int, => B) => B)(implicit r: scalaz.Foldable[Stream]): B। নোট করুন যে বেনামে ফাংশন একটি দ্বিতীয় প্যারামিটারটিকে তার দ্বিতীয় যুক্তি হিসাবে গ্রহণ করে, তাই এটির মূল্যায়ন করার দরকার নেই।

বিটিডব্লিউ, আপনি এখনও এটি ওপির প্যাটার্ন মেলানো সমাধান দিয়ে লিখতে পারেন তবে আমি / অন্যটি এবং আরও মার্জিত মানচিত্রটি পাই।


আপনি যদি printlnআগে প্রকাশ করেন if- elseঅভিব্যক্তি?
মিসিংফ্যাক্টর

@ মিসিংফ্যাক্টর: তারপরে এটি 0 এবং 1 মুদ্রণ করে তবে আরও কিছু নয়
কিম স্টেবল

@ মিসিংফ্যাক্টর: যেহেতু আমার বক্তব্যটি এইভাবে করা সহজ, আমি উত্তরে এটি পরিবর্তন করেছি
কিম স্টেবেল

4
দ্রষ্টব্য যে আপনি যে কোনও পুনরাবৃত্তীয়কে স্ট্রিমে রূপান্তর করতে পারেন toStream, তাই এই উত্তরটি প্রথমে প্রদর্শিত হওয়ার চেয়ে সাধারণ-উদ্দেশ্য।
রেক্স কের

4
আপনি যেহেতু স্কালাজ ব্যবহার করছেন তা কেন ‛০.some use ব্যবহার করবেন না?
পেদ্রোফুরলা

7

ঠিক আছে, স্কেলা অ স্থানীয় লোকসান রিটার্নের অনুমতি দেয়। এটি একটি ভাল শৈলী কিনা তা নিয়ে বিভিন্ন মতামত রয়েছে।

scala> def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
     |   nums.foldLeft (Some(0): Option[Int]) {
     |     case (None, _) => return None
     |     case (Some(s), n) if n % 2 == 0 => Some(s + n)
     |     case (Some(_), _) => None
     |   }
     | }
sumEvenNumbers: (nums: Iterable[Int])Option[Int]

scala> sumEvenNumbers(2 to 10)
res8: Option[Int] = None

scala> sumEvenNumbers(2 to 10 by 2)
res9: Option[Int] = Some(30)

সম্পাদনা:

এই বিশেষ ক্ষেত্রে, যেমন যেমন @ আরজান পরামর্শ দিয়েছেন, আপনি এটি করতে পারেন:

def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
  nums.foldLeft (Some(0): Option[Int]) {
    case (Some(s), n) if n % 2 == 0 => Some(s + n)
    case _ => return None
  }
}

4
পরিবর্তে Some(0): Option[Int]আপনি কেবল লিখতে পারেন Option(0)
লুইজি প্লিঞ্জ

4
@ লুইজিপ্লিনজি, হ্যাঁ আমি কেবল ওপি-র কোডটি কপি-পেস্ট করেছি এবং একটি পয়েন্ট দেওয়ার জন্য প্রয়োজনীয় পর্যাপ্ত পরিবর্তন করেছি।
মিসিংফ্যাক্টর

5

বিড়াল একটি পদ্ধতি বলা foldM যা স্বল্প-পরিক্রমার নেই (জন্য Vector, List, Stream, ...)।

এটি নিম্নলিখিত হিসাবে কাজ করে:

def sumEvenNumbers(nums: Stream[Int]): Option[Long] = {
  import cats.implicits._
  nums.foldM(0L) {
    case (acc, c) if c % 2 == 0 => Some(acc + c)
    case _ => None
  }
}

যদি এটি একটি এমনকি না উপাদান খুঁজে পায় তবে এটি Noneবাকী গণনা না করে ফিরে আসে, অন্যথায় এটি এমনকি সমাপ্তিগুলির যোগফলকে প্রদান করে।

এমনকি যদি আপনি এমনকি একটি এন্ট্রি না পাওয়া পর্যন্ত গণনা রাখতে চান, আপনার একটি ব্যবহার করা উচিত Either[Long, Long]


4

আপনি foldMবিড়ালদের লিবি থেকে ব্যবহার করতে পারেন (@ ডিডাকের পরামর্শ অনুসারে) তবে আপনি যদি প্রকৃত যোগফল পেতে চান তবে তার Eitherপরিবর্তে আমি ব্যবহার করার পরামর্শ দিই Option

bifoldMapফলাফলটি বের করতে ব্যবহার করা হয় Either

import cats.implicits._

def sumEven(nums: Stream[Int]): Either[Int, Int] = {
    nums.foldM(0) {
      case (acc, n) if n % 2 == 0 => Either.right(acc + n)
      case (acc, n) => {
        println(s"Stopping on number: $n")
        Either.left(acc)
      }
    }
  }

উদাহরণ:

println("Result: " + sumEven(Stream(2, 2, 3, 11)).bifoldMap(identity, identity))
> Stopping on number: 3
> Result: 4

println("Result: " + sumEven(Stream(2, 7, 2, 3)).bifoldMap(identity, identity))
> Stopping on number: 7
> Result: 2

একটি অনুরূপ উত্তর পোস্ট করতে এখানে এসেছেন, কারণ এটি আমার মতে এখনও সর্বাধিক সুবিধাজনক এখনও এফপি উপায়। আমি অবাক হয়েছি যে কেউ এর পক্ষে ভোট দেয় না। সুতরাং, আমার +1 ধরুন। (আমি এর (acc + n).asRightপরিবর্তে Either.right(acc + n)তবে যাই হোক না কেন পছন্দ করি )
অ্যাবডলেন্স

বরং bifoldMapঠিক fold(L => C, R => C): Cতেমন কাজ করবে Either[L, R], এবং তারপরে আপনার কোনও দরকার নেইMonoid[C]
বেন হাচিসন

1

@ রেক্স কের আপনার উত্তর আমাকে সাহায্য করেছে, তবে আমি এটি ব্যবহার করতে চাইলে এটি টুইট করতে হবে

  
  Def ফোল্ডঅরফেইল [এ, বি, সি, ডি] (মানচিত্র: বি => হয় [ডি, সি]) (মার্জ: (এ, সি) => এ) (প্রাথমিক: এ) (এটি: অবর্ণনীয় [বি]): হয় [ডি, এ] =
    ভাল ii = it.iterator
    var b = প্রারম্ভিক
    (ii.hasNext) while
      ভাল x = ii.next
      মানচিত্র (এক্স) মিল {
        কেস বাম (ত্রুটি) => বাম ফিরে (ত্রুটি)
        কেস রাইট (ডি) => বি = মার্জ (খ, ডি)
      }
    }
    ডান (খ)
  }

1

আপনি একটি অস্থায়ী ভার ব্যবহার এবং টেকহাইল ব্যবহার করার চেষ্টা করতে পারেন। এখানে একটি সংস্করণ।

  var continue = true

  // sample stream of 2's and then a stream of 3's.

  val evenSum = (Stream.fill(10)(2) ++ Stream.fill(10)(3)).takeWhile(_ => continue)
    .foldLeft(Option[Int](0)){

    case (result,i) if i%2 != 0 =>
          continue = false;
          // return whatever is appropriate either the accumulated sum or None.
          result
    case (optionSum,i) => optionSum.map( _ + i)

  }

evenSumহওয়া উচিত Some(20)এই ক্ষেত্রে।


0

কলিং কোডটিতে এটি পরিচালনা করে আপনার সমাপ্তির মানদণ্ডের মুখোমুখি হওয়ার পরে আপনি একটি সুনির্বাচিত ব্যতিক্রম ছুঁড়ে ফেলতে পারেন।


4
স্কালা ইতিমধ্যে এ জাতীয় বৈশিষ্ট্য নিয়ে আসে: স্কালা
মাল্টে শোয়ারহফ

0

আরও একটি সুন্দর সমাধান স্প্যান ব্যবহার করা হবে:

val (l, r) = numbers.span(_ % 2 == 0)
if(r.isEmpty) Some(l.sum)
else None

... তবে এটি যদি সমস্ত সংখ্যা সমান হয় তবে এটি তালিকাটিকে দুইবার বিভক্ত করে


4
আমি আপনার সমাধান দ্বারা উদাহরণস্বরূপ পার্শ্বীয় চিন্তাভাবনা পছন্দ করি, তবে এটি কীভাবে কোনও ভাঁজটিকে শীঘ্রই বন্ধ করতে হবে তার সাধারণ প্রশ্নের সাথে মোকাবিলা করার পরিবর্তে প্রশ্নের মধ্যে নেওয়া নির্দিষ্ট উদাহরণটি সমাধান করে।
iainmcgin

আমি উল্টোটি কীভাবে করব তা দেখাতে চেয়েছিলাম, কোনও ভাঁজকে তাড়াতাড়ি শেষ না করে কেবল ভাঁজ (এই ক্ষেত্রে যোগফল) আমরা যে মানগুলি ভাঁজ করতে চাই তার চেয়ে বেশি
আরজান

0

কেবলমাত্র "একাডেমিক" কারণে (:

var headers = Source.fromFile(file).getLines().next().split(",")
var closeHeaderIdx = headers.takeWhile { s => !"Close".equals(s) }.foldLeft(0)((i, S) => i+1)

দু'বার লাগে তবে এটি হওয়া উচিত তবে এটি একটি দুর্দান্ত একটি লাইনার। "বন্ধ" পাওয়া না গেলে এটি ফিরে আসবে

headers.size

আর একটি (আরও ভাল):

var headers = Source.fromFile(file).getLines().next().split(",").toList
var closeHeaderIdx = headers.indexOf("Close")
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.