除了对字符串字符进行迭代并比较它们的蛮力方法之外,在Swift中找到两个字符串中最长公共前缀的最惯用方法是什么?
例如,此代码段中commonPrefixWith()
的实现:
let firstString = "The quick brown fox jumps over the lazy dog"
let secondString = "The quick brown fox has a pogo stick"
let result = firstString.commonPrefixWith(secondString) // result == "The quick brown fox "
对于具有非常优雅的功能解决方案的东西有这种感觉,但我看不出方法的最佳起点。
答案 0 :(得分:9)
只是想补充一点,基金会实际上有一种方法(自iOS 8 / macOS 10.10起),它确实如此:
func commonPrefix(with str: String,
options mask: NSString.CompareOptions = []) -> String
请参阅https://developer.apple.com/reference/foundation/nsstring/1408169-commonprefix
虽然这对找到惯用/功能实现方法没有帮助,但它可能会帮助那些只需要完成工作的人。 :)
答案 1 :(得分:5)
这是另一种可能的“功能”方法。作为一种工具,我们需要一种方法 根据谓词“截断”序列。以下用途 来自https://github.com/oisdk/SwiftSequence/blob/master/SwiftSequence/TakeDrop.swift的想法。
首先为生成器类型定义takeWhile
:
extension GeneratorType {
/// Returns a new generator whose `next()` function returns the elements
/// from the given generator as long they satisfy the predicate,
/// and then returns `nil`.
func takeWhile(predicate : (Element) -> Bool) -> AnyGenerator<Element> {
var gen = self
return anyGenerator( { gen.next().flatMap( { predicate($0) ? $0 : nil }) })
}
}
现在“解除”方法以序列类型:
extension SequenceType {
/// Returns a new sequence with all initial elements from the given sequence
/// satisfying the predicate.
func takeWhile(predicate : (Generator.Element) -> Bool) -> AnySequence<Generator.Element> {
return AnySequence( { self.generate().takeWhile(predicate) })
}
}
这可以非常普遍地使用,这是一个简单的例子:
for i in [1, 4, 2, 5, 3].takeWhile( {$0 < 5} ) {
print(i)
}
// Output: 1 4 2
“公共前缀”功能现在可以定义为
extension String {
func commonPrefixWith(other: String) -> String {
return String(zip(self.characters, other.characters).takeWhile({$0 == $1}).map({ $1 }))
}
}
示例:
let firstString = "abc1xy"
let secondString = "abc2x"
let common = firstString.commonPrefixWith(secondString)
print(common) // abc
说明:
zip(self.characters, other.characters)
枚举两个字符序列
并行创建(懒惰评估)的对序列:
("a", "a"), ("b", "b"), ("c", "c"), ("1", "2"), ("x", "x")
.takeWhile({$0 == $1})
将此序列限制为初始部分
两个字符串中的相同字符:
("a", "a"), ("b", "b"), ("c", "c")
.map({ $1 })
将每个元组映射到第二个元素,返回数组
[ "a", "b", "c"]
最后,String(...)
将字符组合成一个字符串。
从 Swift 4开始,序列采用prefix(while:)
方法
布尔谓词,可以在这里使用而不是定义
自定义takeWhile
方法:
extension String {
func commonPrefix(with other: String) -> String {
return String(zip(self, other).prefix(while: { $0.0 == $0.1 }).map { $0.0 })
}
}
字符串也是(再次)它们的字符集合。 (从17.05.2017开始使用Swift 4.0快照测试。)
答案 2 :(得分:2)
我尽量认为功能正常: - ]
extension String {
func commonPrefixWith(another: String) -> String {
let a = Array(self.characters)
let b = Array(another.characters)
return String(
a.enumerate()
.filter { b.count > $0.index && b[0...$0.index] == a[0...$0.index] }
.map { $0.1 }
)
}
}
extension String {
func commonPrefixWith(another: String) -> String {
let b = Array(another.characters)
return String(
Array(self.characters)
.enumerate()
.filter { b.count > $0.index && b[$0.index] == $0.element }
.map { $0.1 }
)
}
}
答案 3 :(得分:1)
这是一个递归函数解决方案,它对字符使用简单的数组操作。我认为这是你正在寻找的一个班轮。
extension String {
func sharedPrefix(with other: String) -> String {
return characters.isEmpty || other.characters.isEmpty ? "" : (characters.first! != other.characters.first! ? "" : "\(characters.first!)" + String(Array(characters.dropFirst())).sharedPrefix(with: String(Array(other.characters.dropFirst()))))
}
}
编辑(通过OP)可能会进一步归结为此,为了便于阅读,尽管不可否认它不再是真正的单行:
extension String {
func sharedPrefix(with other: String) -> String {
return (self.isEmpty || other.isEmpty || self.first! != other.first!) ? "" :
"\(self.first!)" + String(Array(self.dropFirst())).sharedPrefix(with: String(Array(other.dropFirst())))
}
}
答案 4 :(得分:0)
这个问题没有“优雅的功能解决方案”。请记住,你有锤子并不意味着每个问题都是钉子。但是,如果您想使用Swift的高阶函数,可以解决这个问题:
extension String {
func commonPrefixWith(aStr: String) -> String {
var i = 0
var stop = false
return self.characters.reduce("") {
aggregate, char in
let index = self.startIndex.advancedBy(i)
if !stop && index <= aStr.endIndex && char == aStr[index] {
i++
return aggregate + String(char)
} else {
stop = true
return aggregate
}
}
}
}
let firstString = "The quick brown fox jumps over the lazy dog"
let secondString = "The quick brown fox has a pogo stick"
let result = firstString.commonPrefixWith(secondString) // result == "The quick brown fox "
print("'\(result)'")
它不是纯粹的功能,因为它有副作用(i
,stop
),但它很接近。
答案 5 :(得分:0)
这适用于字符串,数组和其他集合。像大多数String操作一样,它返回SubString。如果需要字符串,可以String(result)
。
extension Collection where Element: Equatable {
func shared_prefix( with other: Self ) -> SubSequence {
var a = dropLast(0), b = other.dropLast(0)
while !(a.isEmpty || b.isEmpty)
&& (a.first == b.first) {
( a, b ) = ( a.dropFirst(), b.dropFirst() )
}
return dropLast( a.count )
}
}
用法:
let
a = "It works, which is nice!",
b = "It works and that's a fact.",
result = a.shared_prefix(with:b)
print( result ) // "It works"