6

私の問題は以下のコードで表現されています。.map関数を含む入力を取得しようとしています。.mapを呼び出すと、Intが返されることを知っています。

  // In my case, they are different representations of Ints
  // By that I mean that in the end it all boils down to Int
  val list: Seq[Int] = Seq(1,2,3,4)
  val optInt: Option[Int] = Some(1)
  // I can use a .map with a Seq, check!
  list.map {
    value => println(value)
  }
  // I can use it with an Option, check!
  optInt.map {
    value => println(value)
  }
  // Well, you're asking yourself why do I have to do it,
  // Why don't I use foreach to solve my problem. Check!
  list.foreach(println)
  optInt.foreach(println)

  // The problem is that I don't know what I'm going to get as input
  // The only thing I know is that it's "mappable" (it has the .map function)
  // And that if I were to apply .map it would return Ints to me
  // Like this:
  def printValues(genericInputThatHasMap: ???) {
    genericInputThatHasMap.map {
      value => println(value)
    }
  }

  // The point is, what do I have to do to have this functionality?
  // I'm researching right now, but I still haven't found anything.
  // That's why I'm asking it here =(

  // this works:
  def printValues(genericInputThatHasMap: Seq[Int]) {
    genericInputThatHasMap.map {
      value => println(value)
    }
  }

前もって感謝します!乾杯!

4

3 に答える 3

7

mapまず、とについて簡単に説明しforeachます。コレクション内の各アイテムに対して副作用のある操作(たとえば、標準出力またはファイルへの印刷など)のみを実行することに関心がある場合は、を使用しますforeach。古いコレクションの各要素を変換して新しいコレクションを作成する場合は、を使用しますmap。を書くxs.map(println)と、実際にはコレクションのすべての要素が出力されますが、(完全に役に立たない)ユニットのコレクションも返され、コードの将来の読者(自分自身を含む)を混乱させる可能性がありますforeach。このような状況で使用されます。

さて、あなたの問題に移りましょう。私の意見では、Scala標準ライブラリの最も醜い疣贅の1つに遭遇しました。名前が付けられたメソッドmapforeach(およびflatMap)が言語レベルで魔法のように扱われるという事実は、それらを定義する特定のタイプとは関係ありません。たとえば、私はこれを書くことができます:

case class Foo(n: Int) {
  def foreach(f: Int => Unit) {
    (0 until n) foreach f
  }
}

そして、forこのようなループで使用します。これは、メソッドに名前を付けたからですforeach

for (i <- Foo(10)) println(i)

構造型を使用して、独自のコードで同様のことを行うことができます。

def printValues(xs: { def foreach(f: (Int) => Unit): Unit }) {
  xs foreach println
}

ここxsでは、適切に型指定されたforeachメソッド(anOption[Int]やaなど)をList[Int]使用すると、期待どおりにコンパイルおよび動作します。

map構造型は、作業しようとしているとき、または作業しようとしているときに非常に厄介になりflatMap、他の点では満足できません。たとえば、ランタイムリフレクションを使用するため、醜いオーバーヘッドが発生します。これらの理由による警告を回避するには、実際にはScala2.10で明示的に有効にする必要があります。

seniaの回答が指摘しているように、Scalazライブラリは、のような型クラスを使用することで、問題に対してはるかに一貫性のあるアプローチを提供しますMonad。ただし、次のような場合は使用したくないでしょうMonad。必要以上に強力な抽象化です。、およびをEach提供するために使用します。たとえば、Scalaz7では次のようになります。foreachFunctormap

import scalaz._, Scalaz._

def printValues[F[_]: Each](xs: F[Int]) = xs foreach println

または:

def incremented[F[_]: Functor](xs: F[Int]) = xs map (_ + 1)

要約すると、構造型を使用して、標準的で慣用的な、しかし間違いなく醜い方法でやりたいことを実行できます。または、Scalazを使用してよりクリーンなソリューションを取得できますが、新しい依存関係が犠牲になります。

于 2012-12-15T17:10:17.200 に答える
3

2つのアプローチについての私の考え。

構造タイプ

の構造型を使用できますが、構造型が表示されないためforeach、複数の型で機能するように構造型を作成できます。map例えば:

import collection.generic.CanBuildFrom

object StructuralMap extends App {
  type HasMapAndForeach[A] = {
//    def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That
    def foreach[B](f: (A) ⇒ B): Unit
  }

  def printValues(xs: HasMapAndForeach[Any]) {
    xs.foreach(println _)
  }

//  def mapValues(xs: HasMapAndForeach[Any]) {
//    xs.map(_.toString).foreach(println _)
//  }

  def forComp1(xs: HasMapAndForeach[Any]) {
    for (i <- Seq(1,2,3)) println(i)
  }

  printValues(List(1,2,3))

  printValues(Some(1))

  printValues(Seq(1,2,3))

//  mapValues(List(1,2,3))
}

scala> StructuralMap.main(new Array[String](0))
1
2
3
4
5
6
7
8
9
10

map上でコメントアウトされたメソッドを参照してください。これは、暗黙的Listに型パラメーターとしてハードコーディングされています。CanBuildFromタイプを一般的に拾う方法があるかもしれません-私はそれをそこにあるScalaタイプの達人への質問として残しておきます。とを置き換えてみHasMapAndForeachましthis.typeListが、どちらも機能しませんでした。

構造タイプに関する通常のパフォーマンスの警告が適用されます。

Scalaz

構造タイプは行き止まりなので、サポートしたい場合はmap、Travisのscalazアプローチを見て、それがどのように機能するかを見てみましょう。彼の方法は次のとおりです。

def printValues[F[_]: Each](xs: F[Int]) = xs foreach println

def incremented[F[_]: Functor](xs: F[Int]) = xs map (_ + 1)

(以下では、私が間違っている場合は訂正してください。これをscalaz学習体験として使用しています)

型クラスEachとは、の型を、またはに暗黙的に使用できるFunctor型に制限するために使用されます。たとえば、通話中FEach[F]Functor[F]

printValues(List(1,2,3))

コンパイラは、を満たす暗黙的なものを探しますEach[List]Each特徴は

trait Each[-E[_]] {
  def each[A](e: E[A], f: A => Unit): Unit
}

Eachオブジェクトには、暗黙のfor (Each[TraversableOnce]ListサブタイプでTraversableOnceあり、特性は反変です)があります。

object Each {
  implicit def TraversableOnceEach[A]: Each[TraversableOnce] = new Each[TraversableOnce] {
    def each[A](e: TraversableOnce[A], f: A => Unit) = e foreach  f
  }
}

「コンテキストバインド」構文に注意してください

def printValues[F[_]: Each](xs: F[Int])

の省略形です

def printValues(xs: F[Int])(implicit ev: Each[F])

これらは両方とも、それが型クラスFのメンバーであることを示していEachます。型クラスを満たす暗黙の値は、evパラメーターとしてprintValuesメソッドに渡されます。

printValuesorメソッドの内部では、typeパラメーターに上限または下限がないためincremented、コンパイラーはorメソッドがあることを認識しませんxs。それがわかる限り、コンテキストバウンドを満たします(型クラスの一部です)。持っているスコープには何がありますか? scalazからの両方とメソッドがあります:mapforeachFFAnyRefforeachmapMAforeachmap

trait MA[M[_], A] {
  def foreach(f: A => Unit)(implicit e: Each[M]): Unit = e.each(value, f)

  def map[B](f: A => B)(implicit t: Functor[M]): M[B] = t.fmap(value, f)
}

foreachのおよびmapメソッドは、または型クラスMAによって制約されることに注意してください。これらは元のメソッドと同じ制約であるため、制約が満たされ、メソッドを介して暗黙の変換が行われます。EachFunctorMA[F, Int]maImplicit

trait MAsLow extends MABLow {
  implicit def maImplicit[M[_], A](a: M[A]): MA[M, A] = new MA[M, A] {
    val value = a
  }
}

F元のメソッドのタイプはタイプMインになりMAます。

元の呼び出しに渡された暗黙のパラメーターは、暗黙のパラメーターとしてforeachまたはに渡されますmap。の場合、foreachそのeach暗黙のパラメータで呼び出されますe。上記の例では、元のパラメーターがであったため、暗黙のevはタイプでした。同じタイプです。 に委任する呼び出し。_Each[TraversableOnce]ListeforeacheacheforeachTraversableOnce

したがって、呼び出しの順序は次のprintValues(List(1,2,3))とおりです。

new Each[TraversableOnce]-> printValues-> new MA-> MA.foreach-> Each.each->TraversableOnce.foreach

彼らが言うように、余分なレベルの間接参照で解決できない問題はありません:)

于 2012-12-15T18:19:44.377 に答える
2

から使用MAできますscalaz

import scalaz._
import Scalaz._

def printValues[A, M[_]](ma: MA[M, A])(implicit e: Each[M]) {
  ma |>| { println _ }
}

scala> printValues(List(1, 2, 3))
1
2
3

scala> printValues(Some(1))
1
于 2012-12-15T16:46:21.473 に答える