Scala 在特定位置插入列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4675222/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:43:27  来源:igfitidea点击:

Scala insert into list at specific locations

listscalafunctional-programming

提问by Murgh

This is the problem that I did solve, however being a total imperative Scala noob, I feel I found something totally not elegant. Any ideas of improvement appreciated.

这是我确实解决的问题,但是作为一个完全命令式的 Scala 菜鸟,我觉得我发现了一些完全不优雅的东西。任何改进的想法表示赞赏。

val l1 = 4 :: 1 :: 2 :: 3 :: 4 :: Nil // original list
val insert = List(88,99) // list I want to insert on certain places

// method that finds all indexes of a particular element in a particular list
def indexesOf(element:Any, inList:List[Any]) = {
        var indexes = List[Int]()
        for(i <- 0 until inList.length) {
                if(inList(i) == element) indexes = indexes :+ i
        }
        indexes
}


var indexes = indexesOf(4, l1) // get indexes where 4 appears in the original list

println(indexes)

var result = List[Any]()

// iterate through indexes and insert in front
for(i <- 0 until indexes.length) {
        var prev = if(i == 0) 0 else indexes(i-1)
        result = result ::: l1.slice(prev, indexes(i)) ::: insert
}
result = result ::: l1.drop(indexes.last) // append the last bit from original list

println(result)

I was thinking more elegant solution would be achievable with something like this, but that's just pure speculation.

我认为通过这样的事情可以实现更优雅的解决方案,但这只是纯粹的猜测。

var final:List[Any] = (0 /: indexes) {(final, i) => final ::: ins ::: l1.slice(i, indexes(i))

回答by Rex Kerr

def insert[A](xs: List[A], extra: List[A])(p: A => Boolean) = {
  xs.map(x => if (p(x)) extra ::: List(x) else List(x)).flatten
}

scala> insert(List(4,1,2,3,4),List(88,99)){_ == 4}
res3: List[Int] = List(88, 99, 4, 1, 2, 3, 88, 99, 4)


Edit: explanation added.

编辑:添加了解释。

Our goal here is to insert a list (called extra) in front of selected elements in another list (here called xs--commonly used for lists, as if one thing is xthen lots of them must be the plural xs). We want this to work on any type of list we might have, so we annotate it with the generic type [A].

我们在这里的目标是extra在另一个列表中的选定元素前面插入一个列表(称为)(这里称为 --xs通常用于列表,好像一个东西x那么很多必须是复数xs)。我们希望它适用于我们可能拥有的任何类型的列表,因此我们使用泛型 type 对其进行注释[A]

Which elements are candidates for insertion? When writing the function, we don't know, so we provide a function that says true or false for each element (p: A => Boolean).

哪些元素是插入的候选?在编写函数时,我们不知道,所以我们提供了一个函数,对每个元素 ( p: A => Boolean)表示真或假。

Now, for each element in the list x, we check--should we make the insertion (i.e. is p(x)true)? If yes, we just build it: extra ::: List(x)is just the elements of extrafollowed by the single item x. (It might be better to write this as extra :+ x--add the single item at the end.) If no, we have only the single item, but we make it List(x)instead of just xbecause we want everything to have the same type. So now, if we have something like

现在,对于列表中的每个元素x,我们检查——是否应该进行插入(即是否为p(x)真)?如果是,我们只是构建它:extra ::: List(x)只是extra后面跟单个 item的元素x。(最好将其写为extra :+ x--add the single item at the end。)如果不是,我们只有单个项目,但我们制作它List(x)而不是仅仅x因为我们希望所有内容都具有相同的类型。所以现在,如果我们有类似的东西

4 1 2 3 4

and our condition is that we insert 5 6before 4, we generate

我们的条件是我们在5 6之前插入4,我们生成

List(5 6 4) List(1) List(2) List(3) List(5 6 4)

This is exactly what we want, except we have a list of lists. To get rid of the inner lists and flatten everything into a single list, we just call flatten.

这正是我们想要的,除了我们有一个列表列表。为了摆脱内部列表并将所有内容扁平化为一个列表,我们只需调用flatten.

回答by Landei

The flatten trick is cute, I wouldn't have thought of using maphere myself. From my perspective this problem is a typical application for a fold, as you want go through the list and "collect" something (the result list). As we don't want our result list backwards, foldRight(a.k.a. :\) is here the right version:

扁平化技巧很可爱,我自己不会想到在map这里使用。从我的角度来看,这个问题是折叠的典型应用,因为您想要浏览列表并“收集”某些东西(结果列表)。因为我们不希望我们的结果列表向后,所以foldRight(又名:\)是正确的版本:

def insert[A](xs: List[A], extra: List[A])(p: A => Boolean) = 
  xs.foldRight(List[A]())((x,xs) => if (p(x)) extra ::: (x :: xs) else x :: xs)

回答by David Winslow

Here's another possibility, using Seq#patchto handle the actual inserts. You need to foldRight so that later indices are handled first (inserts modify the indices of all elements after the insert, so it would be tricky otherwise).

这是另一种可能性,Seq#patch用于处理实际插入。您需要 foldRight 以便首先处理后面的索引(插入修改插入后所有元素的索引,否则会很棘手)。

def insert[A](xs: Seq[A], ys: Seq[A])(pred: A => Boolean) = {
  val positions = xs.zipWithIndex filter(x => pred(x._1)) map(_._2)
  positions.foldRight(xs) { (pos, xs) => xs patch (pos, ys, 0) }
}