স্কালায় আমি কীভাবে প্রায় মুছে ফেলি? বা, কেন আমি আমার সংগ্রহগুলির টাইপ পরামিতি পেতে পারি না?


370

এটি স্কেলার জীবনের দুঃখজনক সত্য যে আপনি যদি কোনও তালিকা [ইন্টি] ইনস্ট্যান্ট করেন তবে আপনি যাচাই করতে পারবেন যে আপনার উদাহরণটি একটি তালিকা, এবং আপনি যাচাই করতে পারেন যে এর কোনও স্বতন্ত্র উপাদান একটি অন্তর্গত, তবে এটি নয় যে এটি একটি তালিকা [ ইন্ট], যা সহজেই যাচাই করা যায়:

scala> List(1,2,3) match {
     | case l : List[String] => println("A list of strings?!")
     | case _ => println("Ok")
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!

চেক করা বিকল্প অপসারণের ধরণটি ধীরে ধীরে দোষ চাপিয়ে দেয়:

scala>  List(1,2,3) match {
     |  case l : List[String] => println("A list of strings?!")
     |  case _ => println("Ok")
     |  }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
        case l : List[String] => println("A list of strings?!")
                 ^
A list of strings?!

এটি কেন, এবং আমি কীভাবে এটি ঘিরে পাব?


স্কালা 2.8 বিটা 1 আরসি 4 সবেমাত্র ক্ষয়টি কীভাবে কাজ করে তাতে কিছু পরিবর্তন করা হয়েছে। এটি সরাসরি আপনার প্রশ্নকে প্রভাবিত করে কিনা আমি নিশ্চিত নই।
স্কট মরিসন

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

স্পষ্টতার জন্য ধন্যবাদ - আমি একটি স্কালার নবাগত। আমার মনে হচ্ছে এখনই স্কালায় ঝাঁপিয়ে পড়ার খারাপ সময়। এর আগে, আমি একটি ভাল বেস থেকে ২.৮-র পরিবর্তনগুলি শিখতে পারতাম, পরে আমাকে কখনই পার্থক্যটি জানতে হবে না!
স্কট মরিসন

1
এখানে TypeTagএস সম্পর্কে কিছুটা সম্পর্কিত প্রশ্ন ।
pvorb

2
চলমান scala 2.10.2, পরিবর্তে আমি এই সতর্কতাটি দেখেছি: <console>:9: warning: fruitless type test: a value of type List[Int] cannot also be a List[String] (but still might match its erasure) case list: List[String] => println("a list of strings?") ^আমি আপনার প্রশ্ন এবং উত্তরটি খুব সহায়ক বলে খুঁজে পেয়েছি তবে আমি নিশ্চিত নই যে এই আপডেট হওয়া সতর্কবাণীটি পাঠকদের পক্ষে কার্যকর কিনা।
কেভিন মেরেডিথ

উত্তর:


243

এই উত্তরটি Manifest-API ব্যবহার করে , যা স্কেলা ২.১০ হিসাবে অবহিত করা হয়েছে। আরও বর্তমান সমাধানের জন্য দয়া করে নীচের উত্তরগুলি দেখুন।

স্কালাকে টাইপ ইরেজরের সাথে সংজ্ঞায়িত করা হয়েছিল কারণ জাভা ভার্চুয়াল মেশিন (জেভিএম), জাভা থেকে আলাদা, জেনারিকস পায় নি। এর অর্থ এই যে রান চলাকালীন সময়ে কেবল শ্রেণীর উপস্থিতি রয়েছে, এর ধরণের পরামিতি নয়। উদাহরণস্বরূপ, জেভিএম জানে এটি একটি পরিচালনা করছে scala.collection.immutable.List, তবে এই তালিকাটি প্যারামিটারাইজড নয় Int

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

object Registry {
  import scala.reflect.Manifest

  private var map= Map.empty[Any,(Manifest[_], Any)] 

  def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
    map = map.updated(name, m -> item)
  }

  def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
    map get key flatMap {
      case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
    }     
  }
}

scala> Registry.register("a", List(1,2,3))

scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))

scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None

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

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


3
getপদ্ধতি হিসাবে সংজ্ঞায়িত করা যায় for ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
অ্যারন নভস্ট্রাপ

4
অ্যারন @ খুব ভাল পরামর্শ, তবে আমি আশঙ্কা করি যে এটি স্কালায় তুলনামূলকভাবে নতুনদের জন্য কোডটি অস্পষ্ট করে দিতে পারে। আমি যখন সেই কোডটি লিখেছিলাম তখন আমি নিজেই স্কালার সাথে খুব বেশি অভিজ্ঞতার মুখোমুখি হইনি, যা এই প্রশ্ন / উত্তরটি দেওয়ার আগে এর আগে ছিল।
ড্যানিয়েল সি সোব্রাল

6
@ কিমস্টেবেল আপনি কি জানেন যে TypeTagপ্যাটার্ন ম্যাচিংয়ে স্বয়ংক্রিয়ভাবে ব্যবহৃত হয়? কুল, আহ?
ড্যানিয়েল সি সোব্রাল

1
শান্ত! হতে পারে আপনার উত্তরটি যোগ করা উচিত।
কিম স্টেবল 26'12

1
আমার নিজের প্রশ্নের ঠিক উপরে উত্তর দেওয়ার জন্য: হ্যাঁ, সংকলক Manifestনিজেই পরম তৈরি করেছে , দেখুন: stackoverflow.com/a/11495793/694469 "[ম্যানিফেস্ট / টাইপ-ট্যাগ] উদাহরণ [...] সংকলক দ্বারা স্পষ্টভাবে তৈরি করা হচ্ছে "
কাজম্যাগনুস

96

আপনি টাইপট্যাগ ব্যবহার করে এটি করতে পারেন (যেমন ড্যানিয়েল ইতিমধ্যে উল্লেখ করেছেন, তবে আমি কেবল স্পষ্টভাবে এটি বানান করব):

import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
  case intlist: List[Int @unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}

ক্লাসট্যাগগুলি ব্যবহার করে আপনি এটিও করতে পারেন (যা আপনাকে স্ক্যালাল-প্রতিবিম্বের উপর নির্ভর করে বাঁচায়):

import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
  case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}

ক্লাসট্যাগগুলি এতক্ষণ ব্যবহার করা যেতে পারে কারণ আপনি টাইপ প্যারামিটারটি Aনিজে জেনেরিক টাইপ হওয়ার আশা করেন না ।

দুর্ভাগ্যক্রমে এটি সামান্য ভার্বোজ এবং একটি সংকলক সতর্কতা দমন করতে আপনার @ অচিহ্নযুক্ত টিকাটি প্রয়োজন। টাইপট্যাগটি ভবিষ্যতে সংকলক দ্বারা প্যাটার্ন ম্যাচে স্বয়ংক্রিয়ভাবে সংহত হতে পারে: https://issues.scala-lang.org/browse/SI-6517


2
অপ্রয়োজনীয় অপসারণের বিষয়ে কী [List String @unchecked]এটি এটি এই প্যাটার্ন ম্যাচে কিছু যোগ না করে (কেবল ব্যবহারের মাধ্যমে case strlist if typeOf[A] =:= typeOf[String] =>এটি করা হবে, এমনকি case _ if typeOf[A] =:= typeOf[String] =>বাউন্ডের মধ্যে বাউন্ড ভেরিয়েবলের প্রয়োজন না থাকলেও case)।
নাদের ঘানবাড়ি

1
আমি অনুমান করি যে প্রদত্ত উদাহরণের জন্য কাজ করবে তবে আমি মনে করি বেশিরভাগ প্রকৃত ব্যবহারের উপাদানগুলির ধরণ থাকার ফলে উপকৃত হবে।
tksfz

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

এইচএম না আমি বিশ্বাস করি গার্ডটি প্রয়োগের আগে কোনও castালাই নেই - চেক না করা বিটটি ডানদিকে ডানদিকের কোডটি =>কার্যকর না হওয়া অবধি কোনও অনিঃপৃষ্ঠ । (এবং যখন আরএইচএসের কোডটি কার্যকর করা হয়, তখন রক্ষীরা উপাদানগুলির ধরণের বিষয়ে একটি স্থিতিশীল গ্যারান্টি সরবরাহ করে a সেখানে একটি
beালাই

এই সমাধানটি কি গুরুত্বপূর্ণ রানটাইম ওভারহেড উত্পাদন করে?
stanislav.chetvertkov

65

আপনি ফলাফলের পরে নির্বিঘ্নTypeable থেকে প্রকারের ক্লাসটি ব্যবহার করতে পারেন ,

নমুনা REPL অধিবেশন,

scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._

scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)

scala> l1.cast[List[String]]
res0: Option[List[String]] = None

scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))

castযতটা সম্ভব ইন-স্কোপ দেওয়া অপারেশন সুনির্দিষ্ট wrt ইরেজিওর হবে Typeableদৃষ্টান্ত পাওয়া যায়।


14
এটি নোট করা উচিত যে "কাস্ট" অপারেশনটি পুরো সংগ্রহ এবং এর উপ-সংগ্রহগুলি পুনরাবৃত্তভাবে ঘটাতে হবে এবং জড়িত সমস্ত মান সঠিক প্রকারের কিনা তা যাচাই করবে। ( অর্থাত্ l1.cast[List[String]]মোটামুটি করে for (x<-l1) assert(x.isInstanceOf[String]) বড় ডেটাস্ট্রাকচারের জন্য বা ক্যাসেটগুলি খুব ঘন ঘন ঘটে, এটি একটি অগ্রহণযোগ্য ওভারহেড হতে পারে।
ডোমিনিক আনরুহ

16

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

case class StringListHolder(list:List[String])

StringListHolder(List("str1","str2")) match {
    case holder: StringListHolder => holder.list foreach println
}

এটিতে প্রত্যাশিত আউটপুট রয়েছে এবং আমাদের কেস ক্লাসের সামগ্রীগুলি পছন্দসই ধরণের স্ট্রিং লিস্টের মধ্যে সীমাবদ্ধ করে।

আরও বিশদ এখানে: http://www.scalaified.com/?p=60


14

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


এটি ধরণের ক্ষয়টি কাটিয়ে উঠেনি। তার উদাহরণে, ভ্যাল এক্স করা: যে কোনও = তালিকা (1,2,3); x ম্যাচ {কেস ইন্টারলিস্ট (l) => প্রিন্টলন (গুলি "ম্যাচ $ {l (1);"); কেস _ => প্রিন্টলন (গুলি "কোনও মিল নেই")} "কোনও মিল নেই"
ব্যবহারকারী 48956

আপনি স্কেলা 2.10 ম্যাক্রোগুলি দেখতে পারেন।
অ্যালেক্স

11

অন্যথায় দুর্দান্ত ভাষাটির এই সীমাবদ্ধতার জন্য আমি কিছুটা উন্নততর কাজ পেয়েছি।

স্কালায়, ধরণ মুছে ফেলার বিষয়টি অ্যারে দিয়ে আসে না। আমি মনে করি এটি একটি উদাহরণ দিয়ে প্রদর্শিত সহজ।

আমাদের বলুন যে আমাদের একটি তালিকা রয়েছে (Int, String), তারপরে নিম্নলিখিতটি একটি ধরণের ক্ষয় করার সতর্কতা দেয়

x match {
  case l:List[(Int, String)] => 
  ...
}

এটি নিয়ে কাজ করার জন্য, প্রথমে কেস ক্লাস তৈরি করুন:

case class IntString(i:Int, s:String)

তারপরে প্যাটার্ন ম্যাচিংয়ের মতো কিছু করুন:

x match {
  case a:Array[IntString] => 
  ...
}

যা নিখুঁতভাবে কাজ করে বলে মনে হচ্ছে।

তালিকার পরিবর্তে অ্যারে দিয়ে কাজ করার জন্য এটি আপনার কোডে সামান্য পরিবর্তন প্রয়োজন, তবে এটি কোনও বড় সমস্যা হওয়া উচিত নয়।

নোট করুন যে case a:Array[(Int, String)]ব্যবহারটি এখনও একটি ধরণের ক্ষয়কারী সতর্কতা দেবে, সুতরাং এটি একটি নতুন ধারক শ্রেণি ব্যবহার করা প্রয়োজন (উদাহরণস্বরূপ, IntString)।


10
"অন্যথায় দুর্দান্ত ভাষার সীমাবদ্ধতা" এটি স্কালার সীমাবদ্ধতা এবং জেভিএমের সীমাবদ্ধতা কম। সম্ভবত স্কালাকে জেভিএম-তে চালিত হওয়ায় প্রকারের তথ্য অন্তর্ভুক্ত করার জন্য নকশা করা যেতে পারে, তবে আমি মনে করি না যে এর মতো কোনও নকশা জাভা (যেমন, ডিজাইন হিসাবে আপনি জাভা থেকে স্কালাকে কল করতে পারেন) এর সাথে আন্তঃব্যবহারযোগ্যতা রক্ষা করতে পারে
কার্ল জি


6

যেহেতু জাভা আসল উপাদানের প্রকারটি জানে না, তাই আমি কেবল এটি ব্যবহার করতে সবচেয়ে দরকারী বলে মনে করি List[_]। তারপরে সতর্কবার্তাটি চলে যায় এবং কোডটি বাস্তবতার বর্ণনা দেয় - এটি কোনও অজানা বিষয়ের একটি তালিকা।


4

আমি ভাবছি যে এটি উপযুক্ত কাজের মতো নয়:

scala> List(1,2,3) match {
     |    case List(_: String, _*) => println("A list of strings?!")
     |    case _ => println("Ok")
     | }

এটি "খালি তালিকা" কেসের সাথে মেলে না, তবে এটি একটি সংকলন ত্রুটি দেয়, সতর্কতা নয়!

error: type mismatch;
found:     String
requirerd: Int

অন্যদিকে এটি কাজ করছে বলে মনে হচ্ছে ....

scala> List(1,2,3) match {
     |    case List(_: Int, _*) => println("A list of ints")
     |    case _ => println("Ok")
     | }

এটা কিন্ডা আরও ভাল না আমি এখানে পয়েন্টটি মিস করছি?


3
তালিকার সাথে কাজ করে না (1, "ক", "বি"), যা টাইপ তালিকা রয়েছে [যে কোনও]
sullivan-

1
যদিও স্যালিভানের বক্তব্যটি সঠিক এবং উত্তরাধিকার সংক্রান্ত সম্পর্কিত সমস্যা রয়েছে, তবে আমি এটি দরকারী বলে মনে করি।
শেঠ


0

আমি একটি উত্তর যুক্ত করতে চেয়েছিলাম যা এতে সমস্যাটিকে সাধারণ করে তোলে: রানটাইমে আমার তালিকার ধরণের স্ট্রিং প্রতিনিধিত্ব কীভাবে পাওয়া যায়?

import scala.reflect.runtime.universe._

def whatListAmI[A : TypeTag](list : List[A]) = {
    if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
        println("its a String")
    else if (typeTag[A] == typeTag[Int])
        println("its a Int")

    s"A List of ${typeTag[A].tpe.toString}"
}

val listInt = List(1,2,3)
val listString = List("a", "b", "c")

println(whatListAmI(listInt))
println(whatListAmI(listString))

-18

প্যাটার্ন ম্যাচ গার্ড ব্যবহার করে

    list match  {
        case x:List if x.isInstanceOf(List[String]) => do sth
        case x:List if x.isInstanceOf(List[Int]) => do sth else
     }

4
এটি কেন কাজ করবে না তার কারণ হ'ল isInstanceOfজেভিএম-তে উপলব্ধ ধরণের তথ্যের উপর নির্ভর করে একটি রানটাইম চেক করে। এবং সেই রানটাইম তথ্যটিতে টাইপ আর্গুমেন্ট থাকবে না List(ধরণের ক্ষয়জনিত কারণে)।
ডোমিনিক উরুহ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.