3

我有一个包含 1 和 -1 的列表。我追求的目标是在总数为-1时在列表中找到位置。

List[Int] = List(1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 
-1, 1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1,
 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1)

但是我的代码不起作用。

这是我的尝试(为了更好地阅读,我将代码隔开) 注意:floor是保存 Ints 列表的 val。

floor.foldLeft(0) { ( (x,y) => x+y == -1 ) }.indexOf(-1)

floor.foldLeft(0) ( (x,y) => {  (x + y == -1) {x.indexOf(-1)} }  )

floor.foldLeft(0) { (x,y) => { if (x + y == -1) { indexOf(-1) } } }

我想知道我在这里做错了什么。我真的很想知道为什么,而不仅仅是答案本身。

4

2 回答 2

2

匿名函数( 的第二个参数foldLeft)需要返回与第一个参数相同的类型。

和系列旨在获取集合fold并将reduce其减少为单个值。它在这里对你不起作用。

这会让你得到你想要的。

floor.scanLeft(0)(_+_).indexOf(-1) - 1  // scan collection is 1 element longer

在这种情况下scan,会生成一个具有不同属性/值的新集合,可以查询感兴趣的元素。


所以如果你真的需要使用foldLeft,试试这个。

floor.zipWithIndex.foldLeft((0,-1)) {
  case ((s,x),(e,i)) => if (s+e == -1 && x < 0) (0,i) else (s+e, x)
}._2

非常难看,因为你必须携带当前的总和,s以及你所在位置的索引i,以及正在评估的当前元素e,并且在找到目标之后x,你必须保留它并在最后解包, ._2.

将结果与scanLeft版本进行比较。我想你会发现最后的- 1调整是必要的。


这是另一种方法,如果/当达到所需的目标时,它的好处是尽早退出。

val floorSums:Stream[Int] = Stream.tabulate(floor.length){ idx =>
     floor(idx) + (if (idx>0) floorSums(idx-1) else 0)
}

floorSums.indexOf(-1)  // 38
于 2016-08-11T01:26:34.137 回答
2

对于这样的问题,有两种解决方案,您可能需要在操作的中途退出(在这种情况下是折叠)。

def一种风格是在您为此目的编写的 a 中使用非本地返回(请注意,.zipWithIndex将每个元素转换为一对元素:原始元素加上索引):

def exceedsTen(xs: List[Int]): Int = {
  xs.zipWithIndex.foldLeft(0){ (sum, x) =>
    val now = sum + x._1
    if (now > 10) return x._2
    else now
  }
  -1
}

另一种风格是有一些值,你可以传递它来表明你已经完成了——所以你确实遍历了列表的所有其余部分,但你不做任何工作,因为你知道你没有至。

def overTen(xs: List[Int]): Int = {
  val pair = 
    xs.foldLeft((0, 0)){ (si, x) =>
      if (si._1 > 10) si
      else (si._1 + x, si._2 + 1)
    }
  if (pair._1 > 10) si._2 else -1
}

在这种情况下,保持运行总计和找到所需内容的索引会占用更多空间,但是一旦您熟悉了这两种形式,可以说会更清晰一些。

一般来说,查找事物的索引是一种利基应用;你想做一些事情,而不仅仅是找到一些东西并坚持到以后的位置。

于 2016-08-11T03:54:06.457 回答