没有 var 或 breakable:在数组遍历中遇到谓词时如何“中断”?

Posted

技术标签:

【中文标题】没有 var 或 breakable:在数组遍历中遇到谓词时如何“中断”?【英文标题】:No var or breakable: How to "break" when a predicate is met, in an array traversal? 【发布时间】:2018-05-03 04:20:42 【问题描述】:

如何将此编程逻辑写入函数式方法签名?我正在尝试循环/遍历数组,直到满足条件,然后在该条件下中断。我主要是尽量避免varbreakable 来自scala.util.control.Breaks。它使用闭包(在本例中为dictionary)来检查是否满足条件/谓词。这个想法是我循环遍历一个数组,直到满足谓词。我也避免将我的数组转换为列表。使用数组是否不允许我拼接数组,例如进行模式匹配?

val dictionary = Array.fill(128)(false)

def isUnique(array: Array[Char]): Option[Char] = 

  // traverse each element of the array 
  //     if a character.toInt is in the dictionary, insert into dictionary 
  //        exit loop, with the character which broke the loop
  //     else
  //        set dictionary(character.toInt) to true and continue looping
  // 

这是一个示例用例:

val word = "abcdefggghijklmnopqrstuvqxyz".toArray
val charThatBrokeIt = isUnique(word)

编辑:随意建议或提议其他返回类型,例如布尔、元组、案例类或任何其他类型。 Option[Char] 对我来说可能不是一个好的结果值。例如。我可能已经返回false,以防循环提前(短路)或未发生。

【问题讨论】:

那里的答案使用var。我正在尽力避免var。该链接中唯一不使用 var 的答案来自 fresskoma,它使用长尾递归模式匹配。 如果您对更好的问题标题有可能的建议,请告诉我。它们不是重复的。 返回类型Option[Char] 可能不是最佳类型,因此也可以随意编辑该类型。我知道其他解决方案可能有一些优点/缺点,例如将返回的值包装到可能适用或不适用于模式匹配的案例类中。 【参考方案1】:

首先,String 已经像一个集合,所以你应该只使用String 而不是Array[Char]。其次,您可以利用惰性来允许短路,同时仍将算法拆分为多个部分,使用.view

def breaksUnique(word: String): Option[Char] = 
  val cumulativeSets = word.view.scanLeft(Set.empty[Char])_ + _
  val zipped = cumulativeSets zip word
  val nonDupsDropped = zipped dropWhile case (set, char) => !(set contains char)
  nonDupsDropped.map_._2.headOption

前两行的写法好像是处理整个单词,但是因为它们是在视图上操作的,所以只根据需要计算。

cumulativeSets 是到目前为止已看到的每个字符的集合序列。如果你在“abb”上运行它,你会得到Set(), Set(a), Set(a,b), Set(a,b)。这与使用zip 的原始单词结合,得到(Set(),a), (Set(a),b), (Set(a,b),b)。然后我们只需删除该字符未出现在集合中的所有对,然后返回第一个未删除的元素。

【讨论】:

哇。这是一个非常简洁、令人惊叹的解决方案。我仍在分解其中的大部分内容,但这与我一直在寻找的东西相符!我一直在寻找一个利用短路评估、不变性的解决方案,以及一个使用view 关心运行时间的解决方案。我最喜欢这个解决方案的是你使用了集合,并且可以更清楚地描述预期的函数行为。 dropWhilescanLeft 和 Set 绝对是我不太熟悉的东西,但你向我展示了一个很棒的用例!【参考方案2】:

早期突破总是暗示递归。

def isUnique(array: Array[Char]): Option[Char] = 
  def getDup(index: Int, acc: Set[Char]): Option[Char] =
    if (array.isDefinedAt(index))
      if (acc(array(index))) Some(array(index))
      else getDup(index+1, acc + array(index))
    else None
  getDup(0, Set.empty[Char])

用法:

val word = "abcdefggghijklmnopqrstuvqxyz".toArray
val charThatBrokeIt = isUnique(word)
//charThatBrokeIt: Option[Char] = Some(g)

【讨论】:

Try 在这里似乎有点矫枉过正,因为您知道 getDup 将始终返回 -1 或有效索引。 @JoePallas;这只是将结果包装在 Option 中的一种简洁方法,但是,是的,有更好的方法可以到达那里。 @jwvh 谢谢。我真的希望 Option[Char] 类型不会限制您的解决方案。会有更好的结果吗?我想过返回一个布尔值,但我不确定这是否会增加或减少更多价值。因为如果没有重复的字母,我可以检查 None。 Option[Char] 对我来说似乎很有意义,但理想情况下,方法的配置文件应该由对调用者/客户最有用/最有意义的内容来确定。实施问题应该是次要问题。 返回类型没问题但是方法命名有问题,一个叫做isUnique的方法应该逻辑上返回Boolean

以上是关于没有 var 或 breakable:在数组遍历中遇到谓词时如何“中断”?的主要内容,如果未能解决你的问题,请参考以下文章

数组的声明和遍历

php:循环遍历json数组

数组的定义和遍历

如何循环遍历数组并取消设置数组中的变量

jquery遍历民族json数据

循环遍历数组数组php