【问题标题】:Apply function to one element only in list or array in Scala仅将函数应用于Scala中列表或数组中的一个元素
【发布时间】:2014-11-06 04:26:28
【问题描述】:

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

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)

【问题讨论】:

    标签: arrays list scala scala-collections zipper


    【解决方案1】:

    tl;dr

    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)))
    }
    

    【讨论】:

    • 一个“种子”投票希望你能算出类型参数。收藏一个小时很容易。
    • 当涉及到不适用的隐式时,我永远无法判断这是我的错误还是编译器的错误,因此很难知道何时将其退出。
    • 已修复,虽然我仍然不知道为什么。
    • 很好,但在你重构使用上下文边界之前,我不会给予支持,因为视图边界已被弃用:)
    • 嗯,我不知道视图边界已被弃用。 scala.collection 不包含适当的类型类,是吗?为什么Array 必须如此贱民? :(
    【解决方案2】:

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

    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)
    

    这是我的第一次尝试,正如 Chris 提示的那样:

    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]
    

    晚安,格雷西。

    【讨论】:

      【解决方案3】:

      使用隐式向现有集合添加其他方法非常复杂。如果使用外部方法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 返回一个 Array。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-24
        • 2021-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多