এই সমস্যাটি বোঝার মূল চাবিকাঠিটি এটি উপলব্ধি করে যে সংগ্রহের লাইব্রেরিতে সংগ্রহগুলি তৈরির সাথে কাজ করার জন্য দুটি ভিন্ন উপায় রয়েছে । একটি হ'ল এর সমস্ত দুর্দান্ত পদ্ধতি সহ পাবলিক কালেকশন ইন্টারফেস। অন্যান্য, যা ব্যাপকভাবে ব্যবহার করা হয় তৈরি সংগ্রহের গ্রন্থাগার, কিন্তু যা হয় আর বাইরে প্রায় কখনওই ব্যবহৃত, নির্মাতা নেই।
সমৃদ্ধ করার ক্ষেত্রে আমাদের সমস্যাটি একই ধরণের সংগ্রহগুলি ফিরে আসার চেষ্টা করার সময় সংগ্রহ লাইব্রেরি নিজেই মুখোমুখি হয়। এটি হ'ল, আমরা সংগ্রহগুলি তৈরি করতে চাই, তবে উদারভাবে কাজ করার সময়, "সংগ্রহটি ইতিমধ্যে একই ধরণের" উল্লেখ করার উপায় আমাদের নেই। সুতরাং আমাদের নির্মাতাদের দরকার ।
এখন প্রশ্ন: আমরা আমাদের বিল্ডারগুলি কোথা থেকে পাব? সুস্পষ্ট জায়গাটি সংগ্রহ থেকেই। এটি কাজ করে না । আমরা ইতিমধ্যে সিদ্ধান্ত নিয়েছি, জেনেরিক সংগ্রহের দিকে যাওয়ার আগে আমরা সংগ্রহের ধরণটি ভুলে যাব। সুতরাং যদিও সংগ্রহটি এমন কোনও বিল্ডারকে ফিরিয়ে দিতে পারে যা আমরা যে ধরণের পছন্দ করতে পারি তার আরও সংগ্রহ উত্পন্ন করে, তবে প্রকারটি কী তা তা জানত না।
পরিবর্তে, আমরা CanBuildFrom
চারপাশে ভাসমান প্রভাব থেকে আমাদের বিল্ডারদের পেতে । এগুলি ইনপুট এবং আউটপুট ধরণের সাথে মেলে এবং আপনাকে উপযুক্ত টাইপ করা বিল্ডার দেওয়ার উদ্দেশ্যে বিশেষভাবে বিদ্যমান।
সুতরাং, আমাদের তৈরির জন্য দুটি ধারণামূলক লাফিয়ে উঠেছে:
- আমরা মান সংগ্রহের ক্রিয়াকলাপ ব্যবহার করছি না, আমরা বিল্ডার ব্যবহার করছি।
- আমরা এই বিল্ডারগুলি
CanBuildFrom
সরাসরি আমাদের সংগ্রহ থেকে নয়, অন্তর্নিহিত এস থেকে পাই get
আসুন একটি উদাহরণ তাকান।
class GroupingCollection[A, C[A] <: Iterable[A]](ca: C[A]) {
import collection.generic.CanBuildFrom
def groupedWhile(p: (A,A) => Boolean)(
implicit cbfcc: CanBuildFrom[C[A],C[A],C[C[A]]], cbfc: CanBuildFrom[C[A],A,C[A]]
): C[C[A]] = {
val it = ca.iterator
val cca = cbfcc()
if (!it.hasNext) cca.result
else {
val as = cbfc()
var olda = it.next
as += olda
while (it.hasNext) {
val a = it.next
if (p(olda,a)) as += a
else { cca += as.result; as.clear; as += a }
olda = a
}
cca += as.result
}
cca.result
}
}
implicit def iterable_has_grouping[A, C[A] <: Iterable[A]](ca: C[A]) = {
new GroupingCollection[A,C](ca)
}
চলুন, এটি আলাদা করা যাক। প্রথমত, সংগ্রহের সংগ্রহগুলি তৈরি করতে, আমরা জানি যে আমাদের দুটি ধরণের সংগ্রহ তৈরি করতে হবে: C[A]
প্রতিটি দলের জন্য এবং C[C[A]]
যা সমস্ত দলকে একত্রিত করে। সুতরাং, আমাদের দু'জন নির্মাতা দরকার, একজন যা গ্রহণ A
করে এবং সেগুলি তৈরি করে C[A]
, এবং যেগুলি গ্রহণ C[A]
করে এবং C[C[A]]
এস তৈরি করে । এর স্বাক্ষর প্রকারের দিকে তাকিয়ে CanBuildFrom
আমরা দেখতে পাই
CanBuildFrom[-From, -Elem, +To]
যার অর্থ ক্যানবিল্ডফর্ম আমরা যে ধরণের সংগ্রহের সাথে শুরু করছি তা জানতে চায় - আমাদের ক্ষেত্রে এটি C[A]
এবং তারপরে উত্পন্ন সংগ্রহের উপাদানগুলি এবং সেই সংগ্রহের ধরণটি । সুতরাং আমরা সেগুলি পূরণ করি নিখুঁত পরামিতি হিসাবে cbfcc
এবং cbfc
।
এটি বুঝতে পেরে, এটি বেশিরভাগ কাজ। CanBuildFrom
আমাদের বিল্ডারদের দেওয়ার জন্য আমরা আমাদের গুলি ব্যবহার করতে পারি (আপনাকে যা করতে হবে সেগুলি প্রয়োগ করতে হবে)। এবং একজন নির্মাতা +=
এটির সাথে একটি সংগ্রহ তৈরি করতে পারে , এটি সংগ্রহের সাথে রূপান্তর করতে পারে যা শেষ পর্যন্ত তার সাথে থাকার কথা result
, এবং নিজেই খালি হয় এবং আবার শুরু করার জন্য প্রস্তুত থাকে clear
। নির্মাতারা খালি শুরু করে, যা আমাদের প্রথম সংকলন ত্রুটি সমাধান করে এবং আমরা যেহেতু পুনর্বার পরিবর্তে বিল্ডার ব্যবহার করছি, দ্বিতীয় ত্রুটিটিও চলে যায়।
একটি শেষ সামান্য বিশদ - আসলে কাজটি করে এমন অ্যালগরিদম ব্যতীত - অন্তর্নিহিত রূপান্তর। নোট করুন যে আমরা ব্যবহার করি new GroupingCollection[A,C]
না [A,C[A]]
। এটি কারণ শ্রেণি ঘোষণাটি C
একটি প্যারামিটারের সাথে ছিল , যা এটি এতে A
পাসের সাথে এটি পূরণ করে। সুতরাং আমরা কেবল এটি টাইপ C
করি এবং এটি এটি তৈরি C[A]
করতে দিন । গৌণ বিশদ, তবে আপনি অন্য কোনও উপায়ে চেষ্টা করলে সংকলন-সময় ত্রুটিগুলি পাবেন।
এখানে, আমি "সমতুল্য উপাদানগুলি" সংগ্রহের তুলনায় পদ্ধতিটিকে কিছুটা জেনেরিক করে তুলেছি - বরং, যখনই এর ক্রমিক উপাদানগুলির পরীক্ষা ব্যর্থ হয় তখন পদ্ধতিটি মূল সংগ্রহটি আলাদা করে দেয়।
চলুন আমাদের কার্য পদ্ধতিটি দেখুন:
scala> List(1,2,2,2,3,4,4,4,5,5,1,1,1,2).groupedWhile(_ == _)
res0: List[List[Int]] = List(List(1), List(2, 2, 2), List(3), List(4, 4, 4),
List(5, 5), List(1, 1, 1), List(2))
scala> Vector(1,2,3,4,1,2,3,1,2,1).groupedWhile(_ < _)
res1: scala.collection.immutable.Vector[scala.collection.immutable.Vector[Int]] =
Vector(Vector(1, 2, 3, 4), Vector(1, 2, 3), Vector(1, 2), Vector(1))
এটি কাজ করে!
একমাত্র সমস্যাটি হ'ল আমাদের সাধারণভাবে অ্যারেগুলির জন্য এই পদ্ধতিগুলি উপলভ্য নয়, কারণ এটির জন্য পরপর দুটি বিহীন রূপান্তর প্রয়োজন। এটিকে ঘুরে দেখার বেশ কয়েকটি উপায় রয়েছে যার মধ্যে অ্যারেগুলির জন্য পৃথক অন্তর্নিহিত রূপান্তর লিখন, এতে ingালাই করা WrappedArray
ইত্যাদি।
সম্পাদনা: অ্যারে এবং স্ট্রিংগুলির সাথে ডিল করার জন্য আমার অনুকূল দৃষ্টিভঙ্গি হ'ল কোডটিকে আরও জেনেরিক করা এবং তারপরে পুনরায় আরও নির্দিষ্ট করে দেওয়ার জন্য উপযুক্ত অন্তর্নিহিত রূপান্তরগুলি ব্যবহার করা যাতে অ্যারেগুলিও কাজ করে। এই বিশেষ ক্ষেত্রে:
class GroupingCollection[A, C, D[C]](ca: C)(
implicit c2i: C => Iterable[A],
cbf: CanBuildFrom[C,C,D[C]],
cbfi: CanBuildFrom[C,A,C]
) {
def groupedWhile(p: (A,A) => Boolean): D[C] = {
val it = c2i(ca).iterator
val cca = cbf()
if (!it.hasNext) cca.result
else {
val as = cbfi()
var olda = it.next
as += olda
while (it.hasNext) {
val a = it.next
if (p(olda,a)) as += a
else { cca += as.result; as.clear; as += a }
olda = a
}
cca += as.result
}
cca.result
}
}
এখানে আমরা একটি অন্তর্নিহিত জুড়েছেন আমাদের একটি দেয় Iterable[A]
থেকে C
--for সবচেয়ে সংগ্রহের এই মাত্র পরিচয় হতে হবে (যেমন List[A]
ইতিমধ্যে একটি হল Iterable[A]
), কিন্তু অ্যারে জন্য এটি একটি বাস্তব অন্তর্নিহিত রূপান্তর করা হবে। এবং, ফলস্বরূপ, আমরা প্রয়োজনীয়তাটি ফেলে C[A] <: Iterable[A]
রেখেছি - আমরা মূলত কেবল <%
স্পষ্ট করার জন্য প্রয়োজনীয়তা তৈরি করেছি , তাই আমরা সংকলকটি এটি পূরণ করার পরিবর্তে আমরা এটি ইচ্ছামত ব্যবহার করতে পারি it এছাড়াও, আমাদের সংগ্রহ-সংগ্রহগুলি - - C[C[A]]
স্থিতিশীল যে নিষেধাজ্ঞা শিথিল করে ফেলেছি তা হ'ল D[C]
আমরা যা চাই তা পূরণ করতে আমরা পরে এটি পূরণ করব। যেহেতু আমরা এটি পরে পূরণ করতে যাচ্ছি, আমরা এটিকে ধাপে ধাপে স্তর স্তরের পরিবর্তে শ্রেণির স্তর পর্যন্ত রেখেছি। অন্যথায়, এটি মূলত একই।
এখন প্রশ্ন কীভাবে এটি ব্যবহার করবেন। নিয়মিত সংগ্রহের জন্য, আমরা এটি করতে পারি:
implicit def collections_have_grouping[A, C[A]](ca: C[A])(
implicit c2i: C[A] => Iterable[A],
cbf: CanBuildFrom[C[A],C[A],C[C[A]]],
cbfi: CanBuildFrom[C[A],A,C[A]]
) = {
new GroupingCollection[A,C[A],C](ca)(c2i, cbf, cbfi)
}
যেখানে এখন আমরা C[A]
জন্য C
এবং C[C[A]]
জন্য প্লাগ ইন D[C]
। নোট করুন যে আমাদের কলটিতে সুস্পষ্ট জেনেরিক প্রকারের প্রয়োজন new GroupingCollection
তাই এটি কোন ধরণের সাথে সামঞ্জস্য রাখে তা সোজা রাখতে পারে। ধন্যবাদ implicit c2i: C[A] => Iterable[A]
, এটি স্বয়ংক্রিয়ভাবে অ্যারে পরিচালনা করে।
তবে অপেক্ষা করুন, যদি আমরা স্ট্রিং ব্যবহার করতে চান? এখন আমরা সমস্যায় আছি, কারণ আপনার কাছে "স্ট্রিংয়ের স্ট্রিং" থাকতে পারে না। এটি এখানে অতিরিক্ত বিমূর্তি সাহায্য করে: আমরা D
স্ট্রিংগুলি ধরে রাখতে উপযুক্ত এমন কিছু কল করতে পারি । আসুন বাছাই Vector
করুন এবং নিম্নলিখিতগুলি করুন:
val vector_string_builder = (
new CanBuildFrom[String, String, Vector[String]] {
def apply() = Vector.newBuilder[String]
def apply(from: String) = this.apply()
}
)
implicit def strings_have_grouping(s: String)(
implicit c2i: String => Iterable[Char],
cbfi: CanBuildFrom[String,Char,String]
) = {
new GroupingCollection[Char,String,Vector](s)(
c2i, vector_string_builder, cbfi
)
}
CanBuildFrom
স্ট্রিংয়ের ভেক্টরের বিল্ডিং পরিচালনা করার জন্য আমাদের একটি নতুন দরকার (তবে এটি সত্যই সহজ, যেহেতু আমাদের কেবল কল করা দরকার Vector.newBuilder[String]
) এবং তারপরে আমাদের সমস্ত প্রকার পূরণ করতে হবে যাতে GroupingCollection
সংবেদনশীলভাবে টাইপ করা যায়। নোট করুন যে আমরা ইতিমধ্যে একটি [String,Char,String]
ক্যানবিল্ডফর্মের চারপাশে ভাসছি, তাই চরগুলি সংগ্রহ থেকে স্ট্রিংগুলি তৈরি করা যায়।
আসুন এটি ব্যবহার করে দেখুন:
scala> List(true,false,true,true,true).groupedWhile(_ == _)
res1: List[List[Boolean]] = List(List(true), List(false), List(true, true, true))
scala> Array(1,2,5,3,5,6,7,4,1).groupedWhile(_ <= _)
res2: Array[Array[Int]] = Array(Array(1, 2, 5), Array(3, 5, 6, 7), Array(4), Array(1))
scala> "Hello there!!".groupedWhile(_.isLetter == _.isLetter)
res3: Vector[String] = Vector(Hello, , there, !!)