неявный метод Impl для коллекций в scala breeze

У меня есть псевдоним типа для SortedMap[Int, Double], и я хотел бы иметь неявный, который позволяет мне передавать мои SortedMap некоторым встроенным функциям в мгновение ока, в частности, breeze.stats._ функциям variance и stddev.

Вот рабочий пример без последствий:

package com.soquestion

import breeze.linalg._
import breeze.stats._
import scala.collection.SortedMap
import scala.language.implicitConversions

object proof {
  type Series = SortedMap[Int, Double]

  def example: Double = {
    val s: Series = SortedMap(1 -> 9.0, 2 -> 2.0, 3 -> 5.0, 4 -> 4.0, 5 -> 12.0, 6 -> 7.0, 7 -> 8.0, 8 -> 11.0, 9 -> 9.0, 10 -> 3.0, 11 -> 7.0, 12 -> 4.0, 13 -> 12.0, 14 -> 5.0, 15 -> 4.0, 16 -> 10.0, 17 -> 9.0, 18 -> 6.0, 19 -> 9.0, 20 -> 4.0)

    stddev(s.values)
  }
}

запустить в sbt console

scala> com.soquestion.proof.example
res0: Double = 3.0607876523260447

Я бы хотел не указывать .values, а просто вызывать stddev(s) и variance(s).

Вот что я пробовал

package com.soquestion

import breeze.linalg._
import breeze.stats._
import scala.collection.SortedMap
import scala.language.implicitConversions

object proof {
    // Implicitly convert the SortedMap, or any map, to a DenseVector[Double]
  implicit def series2DenseVector(s: Traversable[(Int, Double)]): DenseVector[Double] = {
    DenseVector(s.map(_._2).toArray)
  }
  type Series = SortedMap[Int, Double]

  def example: Double = {
    val s: Series = SortedMap(1 -> 9.0, 2 -> 2.0, 3 -> 5.0, 4 -> 4.0, 5 -> 12.0, 6 -> 7.0, 7 -> 8.0, 8 -> 11.0, 9 -> 9.0, 10 -> 3.0, 11 -> 7.0, 12 -> 4.0, 13 -> 12.0, 14 -> 5.0, 15 -> 4.0, 16 -> 10.0, 17 -> 9.0, 18 -> 6.0, 19 -> 9.0, 20 -> 4.0)

    stddev(s) // <--- compiler error here
  }
}

но я получаю ошибку компилятора

could not find implicit value for parameter impl: breeze.stats.stddev.Impl[com.soquestion.proof.Series,VR]

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

Я действительно видел вопрос Scala Breeze DenseVector Implicit failure, но я не понимаю, как это применимо к этому сценарию.

Полноформатный ответ на основе ответа @ dlwh ниже на случай, если он кому-то понадобится в будущем

package com.soquestion

import breeze.linalg.support._
import breeze.linalg.support.CanTraverseValues._
import breeze.stats._
import scala.annotation.tailrec
import scala.collection.SortedMap
import scala.language.implicitConversions

object proof {
  type Series = SortedMap[Int, Double]

  def example: Double = {
    // ideally this implicit would go in a scope higher up so it could be
    // brought in wherever it's needed, but this works for a sample
    implicit object SeriesIter extends CanTraverseValues[Series, Double] {
      def isTraversableAgain(from: Series) = true
      def traverse(from: Series, fn: ValuesVisitor[Double]): Unit = {
        @tailrec def traverser(idx: Int, t: Array[Double]): Unit = {
          if (idx == 1) fn.visit(t.head)
          else {
            fn.visit(t.head)
            traverser(idx - 1, t.tail)
          }
        }
        val v: Array[Double] = from.values.toArray
        fn.zeros(0, 0d)
        traverser(v.size, v)
      }
    }

    val s: Series = SortedMap(1 -> 9.0, 2 -> 2.0, 3 -> 5.0, 4 -> 4.0, 5 -> 12.0, 6 -> 7.0, 7 -> 8.0, 8 -> 11.0, 9 -> 9.0, 10 -> 3.0, 11 -> 7.0, 12 -> 4.0, 13 -> 12.0, 14 -> 5.0, 15 -> 4.0, 16 -> 10.0, 17 -> 9.0, 18 -> 6.0, 19 -> 9.0, 20 -> 4.0)
    stddev(s)
  }
}

person Alexander Kahoun    schedule 20.11.2015    source источник
comment
предложил изменение для более простой реализации (на самом деле дважды не проверял, но я думаю, что это правильно)   -  person dlwh    schedule 21.11.2015


Ответы (1)


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

Если вы посмотрите на , вы увидите, что для него требуется реализация variance.Impl, для которой требуется meanAndVariance.Impl, который может быть предоставлен для любого типа с неявным CanTraverseValues[T, Double]. По умолчанию для коллекций неявно присутствует CanTraverseValues, но только для содержащегося в нем типа, а не для значений Map типов scala. Реализация CanTraverseValues ​​и CanMapValues ​​позволит реализовать большинство простых UFunc-функций.

Scala обычно не связывает имплициты, поэтому ваш proof пример не сработал.

person dlwh    schedule 20.11.2015
comment
Спасибо! В итоге я добавил следующее, и он решил, что это красиво неявный объект mapLikeIter extends CanTraverseValues ​​[Series, Double] {def isTraversableAgain (from: Series) = true def traverse (from: Series, fn: ValuesVisitor [Double]): Unit = {@ tailrec def traverser (idx: Int, t: Array [Double]): Unit = {if (idx == 1) fn.visit (t.head) else {fn.visit (t.head) traverser (idx - 1, t.tail)}} val v: Array [Double] = from.values.toArray fn.zeros (0, 0d) traverser (v.size, v)}} - person Alexander Kahoun; 21.11.2015