仅将函数应用于Scala中的列表或数组中的一个元素

时间:2014-09-12 04:24:27

标签: arrays list scala scala-collections zipper

对于任何给定的列表或数组,例如

val list = (1 to 3).toList
val array = (1 to 3).toArray

以及从集合类型映射到集合类型的给定函数,例如

def f(v: Int): Int = v + 10

如何将f应用于listarray的第i个元素,以便

list.myApply(f, ith = 2)
res: List(1,12,3)

以及

array.myApply(f, ith = 2)
res: Array(1,12,3)

3 个答案:

答案 0 :(得分:7)

<强> TL;博士

import scala.collection.SeqLike
import scala.collection.generic.CanBuildFrom

implicit class Seq_[A, Repr, 
    S : ({type L[X] = X => SeqLike[A, Repr]})#L](seq: S) {

  def myApply[B >: A, That](f: A => B, ith: Int)
      (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

<强>讨论

天真的近似:

implicit class Seq_[A](seq: Seq[A]) {
  def myApply(f: A => A, ith: Int): Seq[A] =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

使用示例:

scala> (1 to 3).toList.myApply(_ + 10, ith = 2)
res: Seq[Int] = List(1, 12, 3)

尝试实际解决方案:

implicit class Seq_[A, Repr <: SeqLike[A, Repr]](seq: Repr) {
  def myApply[B >: A, That](f: A => B, ith: Int)
                           (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

不幸的是,隐含的不起作用。我不确定为什么。

scala> Seq_[Int, List[Int]]((1 to 3).toList).myApply(_ + 10, ith = 2)
res: List[Int] = List(1, 12, 3)

scala> Seq_[Int, List[Int]]((1 to 3).toList).myApply(_.toString + "*", ith = 2)
res: List[Any] = List(1, 2*, 3)

编辑:修正了!

implicit class Seq_[A, Repr](seq: SeqLike[A, Repr]) {
  def myApply[B >: A, That](f: A => B, ith: Int)
                           (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

示例:

scala> (1 to 3).toList.myApply(_ + 10, ith = 2)
res: List[Int] = List(1, 12, 3)

scala> (1 to 3).toVector.myApply(Math.pow(2, _), ith = 3)
res: scala.collection.immutable.Vector[AnyVal] = Vector(1, 2, 8.0)

但我刚刚意识到你也想让它适用于Array,这不是SeqLike,所以让我想一些......

啊,Predef隐式转换为ArrayArrayOps,这是SeqLike的子类型,所以我们只需要使用视图绑定。

implicit class Seq_[A, Repr <% SeqLike[A, Repr]](seq: Repr) {
  def myApply[B >: A, That](f: A => B, ith: Int)
                           (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

最后我们有正确的行为:

scala> (1 to 3).toList.myApply(_ + 10, ith = 2)
res: List[Int] = List(1, 12, 3)

scala> (1 to 3).toArray.myApply(Math.pow(2, _), ith = 3)
res: Array[AnyVal] = Array(1, 2, 8.0)

再次编辑 - samthebest告诉我视图边界已被弃用,因此使用this guide我们可以用非常丑陋的上下文绑定来替换它。

implicit class Seq_[A, Repr, 
    S : ({type L[X] = X => SeqLike[A, Repr]})#L](seq: S) {

  def myApply[B >: A, That](f: A => B, ith: Int)
      (implicit bf: CanBuildFrom[Repr, B, That]): That =
    seq.updated(ith - 1, f(seq(ith - 1)))
}

答案 1 :(得分:3)

有人刚问过补丁,所以这可能是重复的:

scala> val list = (1 to 3).toList
list: List[Int] = List(1, 2, 3)

scala> def f(v: Int): Int = v + 10
f: (v: Int)Int

scala> def g(is: Seq[Int], f: Int => Int, i: Int) = is.patch(i, Seq(f(is(i))), 1)
g: (is: Seq[Int], f: Int => Int, i: Int)Seq[Int]

scala> g(list, f, 1)
res1: Seq[Int] = List(1, 12, 3)

概括smidgen:

scala> def g(is: collection.Seq[Int], f: Int => Int, i: Int, count: Int = 1) = is.patch(i, is.slice(i, i + count) map f, count)
g: (is: Seq[Int], f: Int => Int, i: Int, count: Int)Seq[Int]

scala> g(list, f, 1)
res2: Seq[Int] = List(1, 12, 3)

scala> g(list, f, 1, 2)
res3: Seq[Int] = List(1, 12, 13)

这是我的第一次,克里斯提示:

scala> def g(is: collection.mutable.Seq[Int], f: Int => Int, i: Int) = is(i) = f(is(i))
g: (is: scala.collection.mutable.Seq[Int], f: Int => Int, i: Int)Unit

scala> val as = (1 to 10).toArray
as: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> g(as, f, 1)

scala> as
res7: Array[Int] = Array(1, 12, 3, 4, 5, 6, 7, 8, 9, 10)

正如克里斯所说:

scala> def g(is: collection.Seq[Int], f: Int => Int, i: Int) = is.updated(i, f(is(i)))
g: (is: Seq[Int], f: Int => Int, i: Int)Seq[Int]

晚安,格雷西。

答案 2 :(得分:0)

使用implicits向现有集合添加其他方法非常复杂。如果使用外部方法OK,这是一个解决方案。 几乎在那里但不完全。 Scala IDE工作表中的示例

object SeqOps {
  def applyToith(col: Seq[Int], f: Int => Int, ith: Int): Seq[Int] = {
    val indexCol = col.zipWithIndex
    indexCol.map {
      a => if (a._2 == ith) f(a._1) else a._1
    }
  }                                       //> applyToith: (col: Seq[Int], f: Int => Int, ith: Int)Seq[Int]

  def f(i: Int) = i + 10                  //> f: (i: Int)Int
  val l = List(1, 2, 3)                   //> l  : List[Int] = List(1, 2, 3)
  applyToith(l, f _, 0)                   //> res0: Seq[Int] = List(11, 2, 3)

  val a = Array(1, 2, 3)                  //> a  : Array[Int] = Array(1, 2, 3)
  applyToith(a, f _, 1)                   //> res1: Seq[Int] = ArrayBuffer(1, 12, 3)

  val v = Vector(1, 2, 3)                 //> v  : scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)
  applyToith(v, f _, 2)                   //> res2: Seq[Int] = Vector(1, 2, 13)

}

对于数组,它返回ArrayBuffer而不是Array。对于所有其他Seq类型正常工作。我尝试了很多其他组合,但没有解决这个问题。

val a : Seq[Int] = Array(1, 2)
a.zipWithIndex

此zipWithIndex返回一个ArrayBuffer,但如果使用val a: Array[Int]zipWithIndex将返回一个数组。

我猜想将改编的Java数组变成集合的变幻莫测。