这是对我previous question的跟进。
我注意到如果我操纵两个字符串(例如,获取最长公共前缀,计算两个字符串之间的差异等),我倾向于使用zip
(如(s1 zip s2)...
中所示)。
它看起来不错但可能效率低(与命令式代码相比)。这是对的吗 ?也许,如果性能很重要,我应该使用命令式算法。
现在我试图找出两个字符串是否不同。您会建议使用zip
来执行此操作吗?
答案 0 :(得分:3)
使用(s1,s2).zipped
比(s1 zip s2)
稍微高效,因为您不需要创建元组;相反,你创建了两个参数的函数。
更好的效率但不易用是定义自己的自定义专用字符串文件夹:
abstract class StrFold[@specialized T](var result: T) {
def apply(c1: Char, c2: Char): Unit
def done: Boolean
}
def strfold[@specialized T](s1: String, s2: String)(folder: StrFold[T]): T = {
var i = 0
val L = math.min(s1.length, s2.length)
while (i < L) {
folder(s1.charAt(i), s2.charAt(i))
if (folder.done) return folder.result
i += 1
}
folder.result
}
现在,如果你想找到最长公共前缀的长度,你可以
class LcpFind extends StrFold(0) {
var done = false
def apply(c1: Char, c2: Char) { if (c1 == c2) result += 1 else done = true }
}
def lcp(s1: String, s2: String) = strfold(s1,s2)(new LcpFind)
这是一项相当多的工作 - 可以说只是写出命令式或递归式函数代码来计算LCP而不使用fold-with-escape子句。
但它应该几乎与每次手动编写低级字符串的速度一样快。 (唯一的惩罚应该是每次创建(微小的)LcpFind
对象,如果你真的想要通过在调用之间将result
重置为零来重用它,或者你可以修改{{1}和StrFold
实现并调用strfold
方法,将输入参数更改为名为reset
并使用单独的zero
方法。)
答案 1 :(得分:2)
是的,由于两个原因,效率会降低。
您正在创建一个与较短字符串长度相同的字符对列表。这将比原来的两个字符串占用更多的位置。通常的方法是找到最长的公共前缀或检查一个字符中的字符串是否不同,不需要任何额外的内存。
找到LCP时,您不一定需要遍历整个字符串。由于zip
确实需要这样做,因此首先拉链它们的效率会明显降低。
但这并不意味着你必须使用命令式算法:正常的尾递归函数算法也会表现得很好。
答案 2 :(得分:2)
它看起来不错但可能效率低(与命令式代码相比)。
它复制两个输入,因此它实际上是O( n )空间,而找到最长的公共前缀可以是O(1)内存中的时间。此外,它需要O( n )时间,而O(len(LCP))时间就足够了(尽管在最坏的情况下是O( n )时间)记忆分配;这是一项昂贵的操作。
现在我试图找出两个字符串在一个char中是否有所不同。您会建议使用
zip
来执行此操作吗?
取决于字符串的长度以及性能是否至关重要。对于第一次拍摄,可以使用zip
。
答案 3 :(得分:1)
执行view
时,您可以使用zip
进行延迟评估(因此它只会压缩您所拍摄的内容)。