এইচএললিস্টগুলি কি টিউপস লেখার একটি সংশ্লেষিত পদ্ধতি ছাড়া আর কিছুই নয়?


144

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

(আমি জানি যে 22 টি আছে (আমি বিশ্বাস করি) TupleN স্কালায় , যেখানে একজনের কেবলমাত্র একটিমাত্র এইচএললিস্ট প্রয়োজন, তবে এটি আমার আগ্রহী ধারণাবাদী পার্থক্য নয়))

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

প্রেরণা

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

ইন্ট্রো

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

এইচএললিস্ট বনাম টিপলস

যদি এটি সত্য হয়, অর্থাত্, আপনি স্থিতিরূপে নম্বর এবং টাইপ জানেন - প্রশ্ন 2: কেন কেবল একটি এন-টিপল ব্যবহার করবেন না? নিশ্চিত, আপনি typesafely ম্যাপ করতে পারেন এবং একটি HList উপর ভাঁজ (যা আপনিও যে কোনো কিন্তু না typesafely, সাহায্যে একটি tuple উপর না productIterator), কিন্তু নম্বর এবং উপাদান স্ট্যাটিক্যালি পরিচিত ধরণ যেহেতু আপনি সম্ভবত tuple উপাদান অ্যাক্সেস করতে পারে সরাসরি এবং অপারেশন সঞ্চালন।

অন্যদিকে, যদি fআপনি কোনও লিস্টের উপরে ম্যাপ করা ফাংশনটি এত সাধারণ হয় যে এটি সমস্ত উপাদানকে গ্রহণ করে - প্রশ্ন 3: কেন এটি ব্যবহার করবেন না productIterator.map? ঠিক আছে, পদ্ধতি ওভারলোডিং থেকে একটি আকর্ষণীয় পার্থক্য আসতে পারে: যদি আমাদের বেশিরভাগ ওভারলোড fকরা হত, যদি লিস্টের সরবরাহকৃত প্রকারের শক্তিশালী তথ্য থাকত (প্রোডাক্ট আইট্রেটারের বিপরীতে) সংকলকটিকে আরও নির্দিষ্ট নির্দিষ্ট করার সুযোগ দিতে পারে f। তবে, আমি নিশ্চিত নই যে এটি স্ক্যালায় আসলে কাজ করবে কিনা, কারণ পদ্ধতি এবং ক্রিয়াগুলি এক নয়।

এইচলিস্ট এবং ব্যবহারকারী ইনপুট

একই অনুমানের উপর ভিত্তি করে তৈরি করা, যেহেতু আপনাকে স্ট্যাটিক্যালি উপাদানগুলির সংখ্যা এবং প্রকারগুলি জানতে হবে - প্রশ্ন 4: এই লিস্টগুলি এমন পরিস্থিতিতে ব্যবহার করা যেতে পারে যেখানে উপাদানগুলি কোনও ধরণের ব্যবহারকারীর মিথস্ক্রিয়তার উপর নির্ভর করে? উদাহরণস্বরূপ, একটি লুপের ভিতরে উপাদানগুলির সাথে একটি তালিকা তৈরির কল্পনা করুন; নির্দিষ্ট শর্ত ধরে না হওয়া পর্যন্ত উপাদানগুলি কোথাও (ইউআই, কনফিগারেশন ফাইল, অভিনেতা মিথস্ক্রিয়া, নেটওয়ার্ক) থেকে পড়া হয়। তালিকাটি কী ধরণের হবে? একটি ইন্টারফেস স্পেসিফিকেশন getElements জন্য অনুরূপ: এইচএললিস্ট [...] যা স্থির অজানা দৈর্ঘ্যের তালিকাগুলির সাথে কাজ করা উচিত, এবং এটি একটি সিস্টেমের উপাদান A কে উপাদান বি থেকে স্বেচ্ছাসেবী উপাদানগুলির একটি তালিকা পেতে অনুমতি দেয় that

উত্তর:


144

এক থেকে তিনটি প্রশ্নের উদ্দেশ্যে সম্বোধন করা: এর জন্য অন্যতম প্রধান অ্যাপ্লিকেশন HListsহ'ল শালীনতার উপর বিমূর্ত। শালীনতা সাধারণত কোনও বিমূর্তকরণের প্রদত্ত ব্যবহারের সাইটে স্থিতিশীলভাবে পরিচিত তবে এটি সাইট থেকে অন্য সাইটে পরিবর্তিত হয়। নিরাকার উদাহরণ থেকে এটি নিন ,

def flatten[T <: Product, L <: HList](t : T)
  (implicit hl : HListerAux[T, L], flatten : Flatten[L]) : flatten.Out =
    flatten(hl(t))

val t1 = (1, ((2, 3), 4))
val f1 = flatten(t1)     // Inferred type is Int :: Int :: Int :: Int :: HNil
val l1 = f1.toList       // Inferred type is List[Int]

val t2 = (23, ((true, 2.0, "foo"), "bar"), (13, false))
val f2 = flatten(t2)
val t2b = f2.tupled
// Inferred type of t2b is (Int, Boolean, Double, String, String, Int, Boolean)

HLists(বা সমতুল্য কিছু) ব্যবহার না করে টিপল আর্গুমেন্টগুলির আধ্যাত্মিকতা সম্পর্কে বিমূর্ততা ব্যবহার করার জন্য flattenএটির একটিও বাস্তবায়ন সম্ভব নয় যা এই দুটি খুব ভিন্ন আকারের যুক্তি গ্রহণ করতে পারে এবং এগুলি নিরাপদ উপায়ে রূপান্তর করতে পারে।

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

// A pair of arbitrary case classes
case class Foo(i : Int, s : String)
case class Bar(b : Boolean, s : String, d : Double)

// Publish their `HListIso`'s
implicit def fooIso = Iso.hlist(Foo.apply _, Foo.unapply _)
implicit def barIso = Iso.hlist(Bar.apply _, Bar.unapply _)

// And now they're monoids ...

implicitly[Monoid[Foo]]
val f = Foo(13, "foo") |+| Foo(23, "bar")
assert(f == Foo(36, "foobar"))

implicitly[Monoid[Bar]]
val b = Bar(true, "foo", 1.0) |+| Bar(false, "bar", 3.0)
assert(b == Bar(true, "foobar", 4.0))

এখানে কোনও রানটাইম পুনরাবৃত্তি নেই, তবে রয়েছে সদৃশতা রয়েছে , যা HLists(বা সমমানের কাঠামোগুলি) ব্যবহারকে মুছে ফেলতে পারে। অবশ্যই, যদি আপনার পুনরাবৃত্তিমূলক বয়লারপ্লেটের জন্য সহিষ্ণুতা বেশি হয় তবে আপনি যত্ন নেওয়ার প্রতিটি আকৃতির জন্য একাধিক বাস্তবায়ন লিখে একই ফলাফল পেতে পারেন।

তিনটি প্রশ্নে আপনি জিজ্ঞাসা করেছেন "... যদি আপনি কোনও লিস্টের উপরে ম্যাপ ফাংশনটি এত জেনেরিক হয় যে এটি সমস্ত উপাদানকে গ্রহণ করে ... কেন প্রোডাক্টআইট্রেটর.ম্যাপের মাধ্যমে এটি ব্যবহার করবেন না?"। যদি আপনি এইচ এল লিস্টের উপরে ম্যাপ করা ফাংশনটি সত্যই ফর্মের হয় Any => Tতবে ম্যাপিংটি productIteratorআপনাকে পুরোপুরি ভালভাবে পরিবেশন করবে। তবে ফর্মটির কাজগুলিAny => T সাধারণত আকর্ষণীয় নয় (অন্তত, তারা অভ্যন্তরীণভাবে কাস্ট টাইপ না করে থাকে না)। আকারহীন বহুবিধ ফাংশন মানের একটি ফর্ম সরবরাহ করে যা সংকলককে আপনার সম্পর্কে সন্দেহজনকভাবে ঠিক সেইভাবে টাইপ-নির্দিষ্ট মামলাগুলি নির্বাচন করতে দেয়। এই ক্ষেত্রে,

// size is a function from values of arbitrary type to a 'size' which is
// defined via type specific cases
object size extends Poly1 {
  implicit def default[T] = at[T](t => 1)
  implicit def caseString = at[String](_.length)
  implicit def caseList[T] = at[List[T]](_.length)
}

scala> val l = 23 :: "foo" :: List('a', 'b') :: true :: HNil
l: Int :: String :: List[Char] :: Boolean :: HNil =
  23 :: foo :: List(a, b) :: true :: HNil

scala> (l map size).toList
res1: List[Int] = List(1, 3, 2, 1)

আপনার প্রশ্ন চারটি সম্পর্কিত, ব্যবহারকারীর ইনপুট সম্পর্কে, দুটি বিষয় বিবেচনা করতে হবে। প্রথমটি হ'ল পরিস্থিতি যেখানে আমরা গতিশীলভাবে একটি প্রসঙ্গ স্থাপন করতে পারি যা গ্যারান্টি দেয় যে একটি পরিচিত স্থিতিশীল শর্তটি পেয়েছে। এই ধরণের পরিস্থিতিতে আকৃতিহীন কৌশল প্রয়োগ করা পুরোপুরি সম্ভব, তবে স্পষ্টভাবে এই প্রবক্তার সাথে যে স্টেটিক অবস্থা যদি রানটাইমে পাওয়া না যায় তবে আমাদের বিকল্প পথ অনুসরণ করতে হবে। আশ্চর্যজনকভাবে এর অর্থ এই যে গতিশীল অবস্থার সাথে সংবেদনশীল এমন পদ্ধতিতে optionচ্ছিক ফলাফল দিতে হবে। এখানে HLists ব্যবহার করে একটি উদাহরণ দেওয়া হয়েছে,

trait Fruit
case class Apple() extends Fruit
case class Pear() extends Fruit

type FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNil
type APAP = Apple :: Pear :: Apple :: Pear :: HNil

val a : Apple = Apple()
val p : Pear = Pear()

val l = List(a, p, a, p) // Inferred type is List[Fruit]

প্রকারের lতালিকার দৈর্ঘ্য বা এর উপাদানগুলির সঠিক প্রকার ক্যাপচার করে না। তবে, যদি আমরা আশা করি এটির একটি নির্দিষ্ট ফর্ম রয়েছে (উদাহরণস্বরূপ যদি এটি কিছু পরিচিত, নির্দিষ্ট স্কিমার সাথে মানিয়ে নেওয়া উচিত), তবে আমরা সেই সত্যটি প্রতিষ্ঠার চেষ্টা করতে পারি এবং সেই অনুসারে কাজ করতে পারি,

scala> import Traversables._
import Traversables._

scala> val apap = l.toHList[Apple :: Pear :: Apple :: Pear :: HNil]
res0: Option[Apple :: Pear :: Apple :: Pear :: HNil] =
  Some(Apple() :: Pear() :: Apple() :: Pear() :: HNil)

scala> apap.map(_.tail.head)
res1: Option[Pear] = Some(Pear())

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

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

val t1 : (Any, Any) = (23, "foo") // Specific element types erased
val t2 : (Any, Any) = (true, 2.0) // Specific element types erased

// Type class instances selected on static type at runtime!
val c1 = stagedConsumeTuple(t1) // Uses intString instance
assert(c1 == "23foo")

val c2 = stagedConsumeTuple(t2) // Uses booleanDouble instance
assert(c2 == "+2.0")

আমি নিশ্চিত যে @PLT_Borat এর উপর নির্ভরশীলভাবে টাইপ করা প্রোগ্রামিং ভাষা সম্পর্কে তাঁর comments ষিদের মন্তব্যগুলি সম্পর্কে কিছু বলার আছে ;-)


2
আমি আপনার উত্তরের শেষ অংশটি দেখে কিছুটা হতবাক হয়েছি - তবে খুব আগ্রহীও! আপনার দুর্দান্ত উত্তরের জন্য এবং অনেকগুলি রেফারেন্সের জন্য ধন্যবাদ, দেখে মনে হচ্ছে যেন আমি করার মতো প্রচুর পাঠ পেয়েছি :-)
মাল্টে শোয়ারহফ

1
আরেটির উপরে বিমূর্তকরণ অত্যন্ত কার্যকর। দুঃখের বিষয়, স্কেলমক যথেষ্ট নকলের সাথে ভুগছেন কারণ বিভিন্ন FunctionNবৈশিষ্ট্য কীভাবে শালীনতার উপর বিমূর্ততা বজায় রাখতে জানেন না: github.com / ডেভলপ / কোর / এসসিআর / প্রধান /… দুঃখের সাথে আমি এড়ানোর জন্য শেপলেস ব্যবহার করতে পারি এমন কোনও উপায় সম্পর্কে আমি অবগত নই, আমাকে "সত্যিকারের" সাথে মোকাবিলা করা দরকার বলে দেওয়া FunctionNহয়েছে
পল

1
আমি একটি আদর্শ (বেশ কৃত্রিম) উদাহরণ তৈরি করেছি - আদর্শ one.com/sxIw1 - যা প্রথম প্রশ্নের প্রথমদিকে রয়েছে। "গতিশীল ডেটার প্রতিক্রিয়া হিসাবে রানটাইম সময়ে সঞ্চালিত স্ট্যাটিক টাইপিং" এর সংমিশ্রণে এই লিস্টগুলি থেকে কি এই উপকার পাওয়া যাবে? (আমি এখনও নিশ্চিত না যে পরবর্তীকালের সম্পর্কে কী আছে)
মাল্টে শোয়ারহফ

17

কেবল স্পষ্ট করে বলতে গেলে, এইচএললিস্ট মূলত Tuple2উপরে কিছুটা আলাদা চিনির স্ট্যাক ছাড়া আর কিছুই নয় ।

def hcons[A,B](head : A, tail : B) = (a,b)
def hnil = Unit

hcons("foo", hcons(3, hnil)) : (String, (Int, Unit))

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


টিপলসকে এইচলিস্টে ম্যাপ করা যায় এবং যেভাবেই হোক ফিরে আসতে পারে, সুতরাং স্পষ্টভাবে আইসোমরফিজম রয়েছে।
এরিক কাপলুন

10

টিপলসের সাহায্যে আপনি অনেক কিছুই করতে পারেন (ভাল):

  • জেনেরিক প্রিপেন্ড / অ্যাপেনড ফাংশন লিখুন
  • একটি বিপরীত ফাংশন লিখুন
  • একটি কনক্যাট ফাংশন লিখুন
  • ...

আপনি অবশ্যই এটি টিপলস দিয়ে সমস্ত করতে পারেন, তবে সাধারণ ক্ষেত্রে নয়। সুতরাং এইচএললিস্ট ব্যবহার করা আপনার কোডটিকে আরও ডিআরওয়াই করে তোলে।


8

আমি এটি অতি সাধারণ ভাষায় ব্যাখ্যা করতে পারি:

টিপল বনাম তালিকার নামকরণ গুরুত্বপূর্ণ নয়। এইচএললিস্টগুলি HTuples হিসাবে নামকরণ করা যেতে পারে। পার্থক্যটি হ'ল স্কালা + হাস্কেল-এ আপনি একটি টিপল (স্কাল সিনট্যাক্স ব্যবহার করে) এটি করতে পারেন:

def append2[A,B,C](in: (A,B), v: C) : (A,B,C) = (in._1, in._2, v)

যে কোনও ধরণের হুবহু দুটি উপাদানের একটি ইনপুট টিপল নিতে, তৃতীয় উপাদান যুক্ত করতে এবং ঠিক তিনটি উপাদানের সাথে সম্পূর্ণ টাইপযুক্ত টিপলটি ফিরিয়ে দিতে। তবে এটি সম্পূর্ণ ধরণের হয়ে ওঠার পরেও স্পষ্টভাবে ইনপুট / আউটপুট দৈর্ঘ্য নির্দিষ্ট করতে হবে।

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.