Nano Hash - криптовалюты, майнинг, программирование

Как использовать класс case для определения экстрактора?

Я хочу использовать соответствие класса case Seq[Byte] как определено List, но произошла ошибка компилятора.

класс варианта использования с ошибкой компилятора

case class :: (head: Byte, tail: Seq[Byte])        
def doMatchWithCaseClass(queue: Seq[Byte]) = {    
  queue match {
    case h :: t => println("Good!") //can't compile
    case _ => println("God!>_<")
  }
}
doMatchWithCaseClass(Seq(1,2,3))

Ошибка компилятора:

Error:(93, 14) constructor cannot be instantiated to expected type;
 found   : ::
 required: Seq[Byte]
      case h :: t => println("Good!") //can't compile
             ^

ОБНОВЛЕНИЕ с почтовым индексом @isaias-b

  final case class :::: (override val head: Int, override val tail: Seq[Int]) extends Seq[Int] {
    override def length: Int = tail.length + 1
    override def iterator: Iterator[Int] = (head :: tail.toList).toIterator
    override def apply(idx: Int) = {
      1.toByte // just for simple
    }
  }

код совпадения:

  def doMatchWithCaseClass(queue: Seq[Int]) = {
    queue match {
      case h :::: t => println("case class - Good! ^_^")
      case x =>
        println(s"case class - God >_<! $x")
    }
  }

тестовый код:

  doMatchWithCaseClass(Seq(1,2,3))

результат консоли:

> case class - God >_<! List(1, 2, 3)

приведенный выше код не является ошибкой компиляции, но это не мой ожидаемый результат.
Надеюсь, кто-то может указать на ошибку. спасибо


Ответы:


1

Я не уверен на 100%, является ли это желаемым решением, но, похоже, это работает:

case class ::(override val head:Int, override val tail: Seq[Int]) 
  extends Seq[Int] { 
    def iterator = ??? 
    def apply(idx: Int): Int = ???
    def length: Int = ??? 
}

Как сообщает вам ошибка компилятора, он нашел экземпляр типа ::, но требовал Seq[Byte]. Я предоставил образец с Int, который теперь расширяет Seq[Int], что позволило мне сделать еще один шаг.

scala> case class ::(head:Int, tail: Seq[Int]) extends Seq[Int]
<console>:10: error: overriding method head in trait IterableLike of type => Int;
 value head needs `override' modifier
       case class ::(head:Int, tail: Seq[Int]) extends Seq[Int]
                     ^

Поэтому я добавил ключевое слово override, а после очередной ошибки — ключевое слово val. Затем осталось определить 3 заданных абстрактных метода, я предоставил заглушки, которые печатаются на консоли. При этом удалось выполнить следующее:

scala> Seq(1,2,3) match { case ::(a, as) => "list"; case _ => "empty!" }
res5: String = empty!

Надеюсь, это исчезнет при правильной реализации трех необходимых методов. Однако компилятор больше не жалуется...

Обновление 1

@badcook отметил, что...

Ваш класс :::: case не работает, потому что подтипирование идет, так сказать, "неправильным путем"; хотя ваш :::: является Seq, очередь не является :::: и последнее имеет значение при сопоставлении с образцом.

И он прав, расширение всегда кажется странным, когда обычно пытаются отдать предпочтение композиции, а не наследованию. Теперь приведу сравнение из онлайн-книги Мартина Одерски о scala, в котором различия между экстракторами, реализованными с использованием case classes, по сравнению с использованием отдельных методов un/apply и un/applySeq.

Несмотря на то, что классы case очень полезны, у них есть один недостаток: они предоставляют конкретное представление данных.

Отделение экстрактора от представления использует независимость от представления, в его терминах. Возникает вопрос: это трудный выбор из-за технических ограничений или мягкий выбор? В большинстве случаев это не сложный выбор:

Итак, какой из двух методов вы должны предпочесть для сопоставления с образцом? Это зависит. Если вы пишете код для закрытого приложения, case-классы обычно предпочтительнее из-за их преимуществ в краткости, скорости и статической проверке. Если вы решите позже изменить иерархию классов, приложение необходимо будет реорганизовать, но обычно это не проблема. С другой стороны, если вам нужно предоставить тип неизвестным клиентам, экстракторы могут быть предпочтительнее, поскольку они сохраняют независимость представления.

К счастью, вам не нужно решать сразу. Вы всегда можете начать с case-классов, а затем, если возникнет необходимость, перейти к экстракторам. Поскольку шаблоны для экстракторов и шаблоны для классов case выглядят в Scala совершенно одинаково, сопоставление шаблонов в ваших клиентах будет продолжать работать.

Тем не менее, есть часть, описывающая теперь, что есть исключения из этого правила!

Адреса электронной почты, обсуждавшиеся в этой главе, были одним из таких примеров. В этом случае экстракторы - единственный возможный выбор.

Обновление 2

Я снова не уверен на 100%, справедливо ли это и для текущего сценария, говоря, если вы столкнулись со случаем, когда вы столкнулись с этими ограничениями. @badcook, кажется, знает это, потому что он указал еще на одну вещь:

Основываясь на ваших комментариях к другим ответам, кажется, что вы уже знаете об объектах-экстракторах и на самом деле просто хотите посмотреть, может ли класс case избавить вас от необходимости вводить собственные методы unapply и unapplySeq для уже существующего типа.

К сожалению, нет. Вы должны использовать unapply и/или unapplySeq (они не так уж плохи :)).

Позже мы это прояснили, так что теперь я думаю понять это и сам. Я подытожу отрывок из беседы, процитировав себя. Для этого предположим, что у вас есть следующие case class Y(x: X):

Предоставленный метод def unapply(y:Y):X из case class Y строго привязан к своим типам ввода и вывода. Единственный способ поместить в него что-то еще — использовать наследование. Тогда это приводит к неприятностям.

И в моем нынешнем понимании эта беда называется зависимость от представления.

21.02.2016
  • Я понял, что :: должен быть расширен с помощью Seq, и извините, код получился не очень хорошим…, надеюсь, кто-нибудь придет сюда. 21.02.2016
  • Удивительно! Спасибо, что организовали такой ресурс. Как вы сказали, я должен использовать экстрактор, чтобы соответствовать типу Seq. Наконец, что, но, почему? 22.02.2016
  • Я добавил новую информацию в пост и удалил почему. Я хотел знать, почему мы должны выбрать здесь экстрактор, а не классы case. 22.02.2016
  • ха..., вопрос, который вы задали, на самом деле. Я просто готовлю его следующим образом: если вы хотите извлечь абстрактный класс или окончательный класс, класс case не может этого сделать. Поскольку класс case только извлекает класс, он расширяется, кроме того, конечный класс он не может расширять, а абстрактный класс бесполезен для извлечения. Seq является абстрактным, который мы пробовали, а String является окончательным классом. В документе, который вы указали, например, адрес электронной почты, попробуйте извлечь строку адреса в кортеж. Таким образом, в обоих случаях мы должны использовать метод unapply object сопутствующих для извлечения данных. 22.02.2016
  • а какая репрезентационная зависимость? это чувство такое абстрактное 22.02.2016

  • 2

    Вы можете напрямую сопоставить Seq с немного другим экстрактором.

    def doMatch(queue: Seq[Byte]) = {
      queue match {
        case h +: t => println(s"Head: $h and tail: $t")
        // You could also do case Nil =>...
        case t      => println(s"Bad: $t")
      }
    }
    
    doMatch(List(1, 2, 3)) // Head: 1 and tail: List(2, 3)
    doMatch(List())        // Bad: List()
    

    Изменить:

    Кажется, вы не просто пытаетесь сопоставить Seqs, но имеете в виду более общее сопоставление с образцом.

    Основываясь на ваших комментариях к другим ответам, кажется, что вы уже знаете об объектах-экстракторах и на самом деле просто хотите посмотреть, может ли case class сэкономить вам усилия по вводу собственных методов unapply и unapplySeq для уже существующего типа.

    К сожалению, нет. Вы должны использовать unapply и/или unapplySeq (они не так уж плохи :)).

    Ваш класс case :::: не работает, потому что подтипирование идет, так сказать, "неправильным путем"; хотя ваш :::: является Seq, queue не является ::::, и последнее имеет значение при сопоставлении с образцом.

    Сопоставитель шаблонов видит ваш первый case и пытается найти соответствующий unapply метод. К сожалению, сгенерированный метод unapply для :::: имеет сигнатуру :::: => Option[(Int, Seq[Int])], а queue не является ::::, поэтому совпадение не удается.

    С другой стороны

    object JustWriteUnapplyAndLifeWillBeGood {
      def unapply(xs: Seq[Int]): Option[(Int, Seq[Int])] =
        if (xs.isEmpty) None else Some(xs.head, xs.tail)
    }
    

    работает просто отлично.

    Seq(1, 2, 3) match {
      case JustWriteUnapplyAndLifeWillBeGood(head, tail) => 
        println(s"head: $head, tail: $tail")
    } // head: 1, tail: List(2, 3)
    
    // Look Ma, no prefix!
    Seq(1, 2, 3) match {
      case head JustWriteUnapplyAndLifeWillBeGood tail =>
        println(s"head: $head, tail: $tail")
    } // head: 1, tail: List(2, 3)
    

    или override сгенерированный unapply метод :::: с правильной подписью.

    21.02.2016
  • Можете объяснить, почему в данном случае необходимо использовать apply и unapply? Каковы точные ограничения? Смотрите также мой ответ для некоторых заслуживающих доверия ресурсов. 21.02.2016
  • Класс case Y по сути является синтаксическим сахаром для набора предопределенных методов, каждый из которых имеет аргументы и/или выходные данные типа Y. Для любых методов, принимающих Y в качестве аргумента (например, unapply), это означает, что для того, чтобы метод можно было вызвать для типа X, X должен быть подтипом Y (т. е. контравариантность). Если X уже существует и еще не extend Y, у Scala нет возможности сделать это задним числом без изменения определения X. В этом случае у пользователя нет возможности изменить Seq, поэтому ни один класс Y не может иметь правильный метод unapply. 21.02.2016
  • Как-то имеет смысл. Предоставленный метод def unapply(y:Y):X из case class Y строго привязан к своим типам ввода и вывода. Единственный способ поместить в него что-то еще — использовать наследование. Тогда это приводит к неприятностям. 21.02.2016
  • о, я думаю, вы указываете на важные вещи, что Seq является абстрактным классом, но Seq(1,2,3) является List, поэтому класс case не может всегда соответствовать Seq, не так ли? 22.02.2016
  • Еще я хочу сказать, что совпадение с образцом не поддерживает покрытие. но я думаю, что это неплохой вариант, если он покрывает. По крайней мере, Seq может сопоставить все его подклассы с моим классом case.:) 22.02.2016
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..