Scala基于Stream生成多句子的实现及StackOverflow报错解决
踩坑点拆解
- 栈溢出的直接原因:你之前写的
def sentences: List[String] = sentences.::(xxx)是完全没有终止条件的无限递归——构造列表的时候不停把新元素塞回自己前面,没有任何退出分支,一调用就直接把栈打满了。 - 取多句时内容异常拼接的核心原因:你复用了同一个全局创建的
generateWord流实例。Scala 2.12里的Stream是带内存记忆化的惰性结构,一旦你从同一个流实例取过N个元素,流的读取位置就会停在第N位,下次再从这个流取元素时,会接着之前的位置往后读,不会从头开始;多次累计取值后,流内部缓存的内容会越来越长,一旦你操作不当把整个缓存的内容转成字符串,看起来就像第一个句子被不停追加新内容。 - 逻辑误区:
Stream.continually(xxx)的语义是每次需要读取下一个流元素时,重新计算xxx的值,只要你传入的计算逻辑没有共享的状态/记忆化的流实例,就会每次生成完全独立的结果。
可直接运行的实现方案
核心修正原则:每次生成句子时,都做独立的随机取值,不要复用带记忆的全局流实例,从根源上避免位置偏移和内容污染。
1. 调整基础生成逻辑
把原来全局创建的带记忆问题的generateWord,改成按需生成的独立单词方法,不再持有全局流状态:
// 按需生成单个随机单词,长度2-25个字符 def generateWord(): String = { (Random.alphanumeric take between(2, 25)).mkString } // 无限句子流:每个元素都是独立的完整句子 def generateSentenceStream: Stream[String] = { // 生成单句:句子长度随机为2-25个单词 def createOneSentence(): String = { val wordCount = between(2, 25) (1 to wordCount).map(_ => generateWord()).mkString(" ") } // 每次取下一个元素时,独立生成新句子,无共享状态 Stream.continually(createOneSentence()) }
2. 生成多句子并存入指定集合
流的状态隔离完成后,直接按需取指定数量的句子,转成对应集合即可:
def main(args: Array[String]): Unit = { val needCount = 5 // 自定义需要生成的句子数量 // 转成数组存储 val sentenceArray: Array[String] = generateSentenceStream.take(needCount).toArray // 转成列表存储 val sentenceList: List[String] = generateSentenceStream.take(needCount).toList // 验证输出:每个句子都是独立随机的内容 sentenceArray.foreach(println) }
3. 兼容你之前尝试的Stream[List[String]]返回值
如果需要每个句子以单词列表的形式存储,不需要拼接成完整字符串,可以按下面的方式实现,完全避开之前的栈溢出问题:
def generateSentenceWordListStream: Stream[List[String]] = { def createOneSentenceWordList(): List[String] = { val wordCount = between(2, 25) (1 to wordCount).map(_ => generateWord()).toList } Stream.continually(createOneSentenceWordList()) }
注意:你之前实现的
between随机数方法完全不需要改动,保持原逻辑即可;如果后续升级到Scala 2.13+,可以把Stream替换为官方推荐的LazyList,内存占用会更低。
内容的提问来源于stack exchange,提问作者NoobZik
相关产品推荐
相关产品推荐

