没有 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 【问题描述】:如何将此编程逻辑写入函数式方法签名?我正在尝试循环/遍历数组,直到满足条件,然后在该条件下中断。我主要是尽量避免var
和breakable
来自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
关心运行时间的解决方案。我最喜欢这个解决方案的是你使用了集合,并且可以更清楚地描述预期的函数行为。 dropWhile
、scanLeft
和 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:在数组遍历中遇到谓词时如何“中断”?的主要内容,如果未能解决你的问题,请参考以下文章