Scala 如何使用函数编程返回列表中的所有正数和第一个负数?

Scala 如何使用函数编程返回列表中的所有正数和第一个负数?,scala,functional-programming,Scala,Functional Programming,假设我有一个未排序的正整数和负整数列表。我想返回一个包含所有正整数和第一个负数的列表,但在保留顺序的同时忽略列表中所有后续负数 我必须做到: l = [1, 2, -4, 5, -6, -1, 3] out = [] first = true for n in l: if n >= 0: out.push(n) else if first: out.push(n) first = false // out = [1, 2,

假设我有一个未排序的正整数和负整数列表。我想返回一个包含所有正整数和第一个负数的列表,但在保留顺序的同时忽略列表中所有后续负数

我必须做到:

l = [1, 2, -4, 5, -6, -1, 3]
out = []
first = true
for n in l:
    if n >= 0:
        out.push(n)
    else if first:
        out.push(n)
        first = false

// out = [1, 2, -4, 5, 3]
我如何在Scala中使用FP实现这一点?我在想(可能不会编译…):

val l=List(1,2,-4,5,-6,-1,3)
val posl=l.map(>=0)
val negl=l.zipWithIndex.map((n,i)=>if(n<0)(i,n)else(None,None)).head
//现在在negl.\u 1拆分posl,并创建一个新的leftSlice::negl.\u 2::rightSlice列表?

这是正确的方法,还是有更优雅、简洁的方法?

如果你想保持顺序(即负片的位置),你可以这样做:

val list = List(1, 2, -4, 5, -6, -1, 3)
val negIndex = list.indexWhere(_ < 0)
val positive = list.zipWithIndex.filter { case (num, index) =>
    num >= 0 || index == negIndex
}.map(_._1)
val list=list(1,2,-4,5,-6,-1,3)
val negIndex=list.indexWhere(<0)
val positive=list.zipWithIndex.filter{case(num,index)=>
num>=0 | | index==negIndex
}.map(u.u 1)
消极的要求使得它很难保持更简洁。我的策略是只使用
索引获取第一张底片的索引,其中
如果没有索引,则为-1,然后
过滤列表中的所有底片,除了第一张底片的索引。如果指数为-1,则无伤害,无犯规。

您可以尝试:

val list = List(1, 2, -4, 5, -6, -1, 3)
val index = list.indexWhere(_ < 0) + 1
list.take(index) ++ list.drop(index).filter(_ > 0)
val list=list(1,2,-4,5,-6,-1,3)
val index=list.indexWhere(<0)+1
list.take(index)+list.drop(index.filter)(\u0)

如果您不介意保留一些状态,您可以一次完成—请注意
var neg

var neg = false
list.filter { 
    case x if x > 0  => true
    case _ if !neg   => neg = true
}

这里有一个尾部递归方法。与m-z的答案相比,它只迭代列表一次,与Dimas答案相比,它不使用可变状态,因此它是纯函数的

def firstNegAllPos(list: List[Int]) : List[Int] = {
  def loop(in: List[Int], out: List[Int], negfound: Boolean) : List [Int] = {
    in match {
      case Nil => out
      case head :: tail =>
        if (negfound)
          loop(tail, if (head < 0) out else head :: out, true)
        else
          loop(tail, head :: out, head < 0)
    }
  }
  loop(list, Nil, false)
}

firstNegAllPos(List(1, 2, -4, 5, -6, -1, 3)) // List(3, 5, -4, 2, 1)
def firstNegAllPos(list:list[Int]):list[Int]={
def循环(in:List[Int],out:List[Int],negfund:Boolean):List[Int]={
相配{
案例Nil=>out
案例头部::尾部=>
如果(未找到)
循环(tail,if(head<0)out else head::out,true)
其他的
环(尾部,头部::向外,头部<0)
}
}
循环(列表,无,假)
}
firstNegAllPos(List(1,2,-4,5,-6,-1,3))//List(3,5,-4,2,1)
编辑

上述实现提供了相反的结果。为了保持秩序,您可以执行以下操作:

def firstNegAllPos(list: List[Int]) : List[Int] = {
  def loop(in: List[Int], out: List[Int], negfound: Boolean) : List [Int] = {
    in match {
      case Nil => out
      case head :: tail =>
        if (negfound)
          loop(tail, if (head < 0) out else head :: out, true)
        else
          loop(tail, head :: out, head < 0)
    }
  }
  loop(list, Nil, false).reverse
}

firstNegAllPos(List(1, 2, -4, 5, -6, -1, 3)) // List(1, 2, -4, 5, 3)
def firstNegAllPos(list:list[Int]):list[Int]={
def循环(in:List[Int],out:List[Int],negfund:Boolean):List[Int]={
相配{
案例Nil=>out
案例头部::尾部=>
如果(未找到)
循环(tail,if(head<0)out else head::out,true)
其他的
环(尾部,头部::向外,头部<0)
}
}
循环(列表,无,假)。反转
}
firstNegAllPos(List(1,2,-4,5,-6,-1,3))//List(1,2,-4,5,3)

下面是保留顺序的尾部递归解决方案:

  def posNeg(xs: List[Int]): List[Int] = {
    @tailrec
    def go(tail: List[Int], res: List[Int]): List[Int] = {
      tail match {
        case h :: t =>
          if (h >= 0) go(t, h :: res)
          else (h :: res).reverse ::: t.filter(_ > 0)
        case _ => res
      }
    }
    go(xs, Nil)
  }

需求的直接翻译非常清楚,只需一次就可以完成列表,而且功能强大:

val l = List(1, 2, -4, 5, -6, -1, 3) 

// split into any initial positive numbers, and the rest of the list 
val (pos, firstneg) = l.span(_ >= 0)

// if there were no negative numbers, return the original list.
// otherwise, it's the initial positives, the first negative, and
// the positive numbers from the rest of the list.
if (firstNeg.isEmpty) l else pos:::List(firstneg.head):::firstneg.tail.filter(_>=0)
//> res0: List[Int] = List(1, 2, -4, 5, 3)

(围绕
firstneg.head
列表
只是为了对称
两侧)

如果没有一个稍微太聪明的递归+模式匹配答案,这将不是一个合适的函数式编程问题

def firstNegAllPos(l:List[Int]):List[Int] = {
   l match{
      case x::xs if x>=0 => x::firstNegAllPos(xs)
      case x::xs if x<0 => x::xs.filter(_>=0)
      case Nil => Nil
   }
}
def firstNegAllPos(l:List[Int]):List[Int]={
我匹配{
如果x>=0=>x::firstNegAllPos(xs),则案例x::xs
如果x::xs.filter(>=0),则案例x::xs
案例Nil=>Nil
}
}

这是一个明显的折叠式操作

val l = List(1, 2, -4, 5, -6, -1, 3)

var result = l.foldLeft((true, Vector.empty[Int])) {
  case ((f, r), x) if x >= 0 => (f, r :+ x)
  case ((f, r), x) if f => (false, r :+ x)
  case ((f, r), x) => (f, r)
}._2

println(result)  // Vector(1, 2, -4, 5, 3)

我使用向量作为中间结构;如果需要,可以使用
toList
将其转换为列表。或者你可以用它来代替向量,但你必须颠倒加法顺序(
r:+x
=>
x::r
),然后用
reverse
方法将列表倒过来。

有很多尾部递归解,但它们都比需要的长

def oneNeg(xs: List[Int]): List[Int] = {
  def loop(in: List[Int], out: List[Int], neg: Int): List[Int] = in match {
    case Nil => out
    case x :: rest =>
      if (neg < 0 && x < 0) loop(rest, out, neg)
      else loop(rest, x :: out, x min neg)
  }
  loop(xs, Nil, 0).reverse
}
def oneeg(xs:List[Int]):List[Int]={
def循环(in:List[Int],out:List[Int],neg:Int):List[Int]=匹配中{
案例Nil=>out
案例x::rest=>
if(负<0&&x<0)循环(剩余、输出、负)
else循环(静止,x::out,x最小负)
}
循环(xs,Nil,0)。反向
}
如果这不是一个面向公众的API,您可以通过仅公开内部方法来缩短它:

def oneNeg(in: List[Int], out: List[Int] = Nil, neg: Int = 0): List[Int] = 
  in match {
    case Nil => out.reverse
    case x :: rest =>
      if (neg < 0 && x < 0) oneNeg(rest, out, neg)
      else oneNeg(rest, x :: out, x min neg)
  }
def oneeg(in:List[Int],out:List[Int]=Nil,neg:Int=0):List[Int]=
相配{
案例Nil=>out.reverse
案例x::rest=>
如果(负<0&&x<0)ONEEG(剩余、输出、负)
else ONEEG(其余,x::输出,x最小负)
}

以下是我在中看到的@sschaef最简洁答案的改进:


是的,这样做了,获取第一个负元素的索引(隐式-->在此之前的所有元素)。引用他的句子:“我想返回一个包含所有正整数和第一个负数的列表”,当我在REPL中运行此操作时,我只得到列表(1,2,-4)作为结果(这将l.indexWhere更改为list.indexWhere)。列表没有排序别忘了,我想保留原来的排序。那只是无缘无故地做更多的传递。您的
list.take
list.takeWhile(\u>0)
(没有理由预先计算索引)更好,因为您正在进行拖放(比dropWhile更好(此外,您还可以使用其他一些解决方案已有的
span
。如果您真的想使用索引,请参阅
splitAt
。这比我的尝试要好,但采用了类似的方法。我想没有办法像命令式方法那样通过一次遍历列表来完成此操作。您可以在其中包含
indexWhere
过滤器以某种方式,但这并不是更好的方法,当然也不是更有效的方法。尽管如此,我仍然不认为这是一种强制性的方法。@jbrown有一个
def oneNeg(xs: List[Int]): List[Int] = {
  def loop(in: List[Int], out: List[Int], neg: Int): List[Int] = in match {
    case Nil => out
    case x :: rest =>
      if (neg < 0 && x < 0) loop(rest, out, neg)
      else loop(rest, x :: out, x min neg)
  }
  loop(xs, Nil, 0).reverse
}
def oneNeg(in: List[Int], out: List[Int] = Nil, neg: Int = 0): List[Int] = 
  in match {
    case Nil => out.reverse
    case x :: rest =>
      if (neg < 0 && x < 0) oneNeg(rest, out, neg)
      else oneNeg(rest, x :: out, x min neg)
  }
val l = List(1, 2, -4, 5, -6, -1, 3)

l.span(_>=0) match { case (left, Nil)      => left
                     case (left, x::right) => left ++ (x +: right.filter(_>=0)) }