gpt4 book ai didi

scala - StringTokenizer 到 Scala 迭代器

转载 作者:行者123 更新时间:2023-12-05 02:26:49 24 4
gpt4 key购买 nike

我正在尝试将 java.util.StringTokenizer 转换为 Scala 的 Iterator,但以下方法失败了:

def toIterator(st: StringTokenizer): Iterator[String] =
Iterator.continually(st.nextToken()).takeWhile(_ => st.hasMoreTokens()))

但这行得通:

def toIterator(st: StringTokenizer): Iterator[String] =
Iterator.fill(st.countTokens())(st.nextToken())

您可以在 Scala 控制台中看到:

scala> Iterator("a b", "c d").map(new java.util.StringTokenizer(_)).flatMap(st => Iterator.continually(st.nextToken()).takeWhile(_ => st.hasMoreTokens())).toList
res1: List[String] = List(a, c)

scala> Iterator("a b", "c d").map(new java.util.StringTokenizer(_)).flatMap(st => Iterator.fill(st.countTokens())(st.nextToken())).toList
res2: List[String] = List(a, b, c, d)

如您所见,res1 不正确,res2 正确。我究竟做错了什么?第一个版本应该可以工作并且是首选,因为它比第二种方法快 2 倍,因为它不会扫描字符串两次

最佳答案

takeWhile 不打算有状态地使用。它应该采用一个纯函数来确定是否继续,完全基于输入

具体来说,迭代器必须在 takeWhile 谓词被调用之前产生值。即使您的函数忽略了 takeWhile 参数,它仍然会被计算。所以 nextToken 被调用,然后我们检查更多的 token 。

准确地说,在您的 "a b" 案例中,

  1. 首先,我们调用nextToken,这是Iterator.continually 所做的。有下一个标记,因此它返回 "a"
  2. 现在,为了确定我们是否应该包含下一个标记,我们调用您的谓词并将“a” 作为参数。您的谓词忽略 "a" 并调用 hasMoreTokens。我们的 tokenizer 有更多的标记(即 "b"),所以它返回 true。继续。
  3. 现在我们再次调用nextToken。这将返回 "b"
  4. 我们需要确定是否应该将其包含在我们的结果中,因此我们的 takeWhile 谓词以 "b" 作为参数运行。我们的 takeWhile 谓词忽略其参数并调用 hasMoreTokens。我们没有更多的 token 了,因此返回 false。我们不应包含此元素。
  5. takeWhile 已返回 false,因此我们在最后一个返回 true 的元素处停止。我们的结果列表是 List("a")

由于我们滥用了像 takeWhile 这样的纯函数技术来实现有状态,所以我们得到了不直观的结果。

尽管拥有一个单行解决方案看起来时髦而聪明,但您拥有的是一个有状态的命令性对象,您希望它适应 Iterator 接口(interface)。将状态隐藏在一堆纯函数调用中并不是一个好主意,因此我们应该编写自己的 Iterator 子类并正确执行。

import java.util.StringTokenizer

final class StringTokenizerIterator(
private val tokenizer: StringTokenizer
) extends Iterator[String] {

def hasNext: Boolean = tokenizer.hasMoreTokens

def next(): String = tokenizer.nextToken()

}

object Example {

def toIterator(st: StringTokenizer): Iterator[String] =
new StringTokenizerIterator(st)

def main(args: Array[String]) = {
println(Iterator("a b", "c d")
.map(new java.util.StringTokenizer(_))
.flatMap(toIterator(_))
.toList)
}

}

我们在调用适当的 StringTokenizer 函数时所做的工作与您所做的相同,但我们在封装状态的完整类中进行,而不是假装状态部分不存在。它是更长的代码,是的,但它应该更长。我们不希望它的困惑部分被忽视。

关于scala - StringTokenizer 到 Scala 迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73638959/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com