为了在功能样式中使用Swift,我们应该如何处理列表的head
和tail
? Array
和ArraySlice
是否合适(看起来像是ArraySlice
是获取子列表的有效机制)?是将Array
转换为ArraySlice
并使用.first!
和.dropFirst()
作为head
和tail
的等价物的正确机制吗?
作为添加数字列表的示例:
func add(_ nums: ArraySlice<Int>) -> Int {
if nums.count == 0 {
return 0
} else {
return nums.first! + add(nums.dropFirst())
}
}
答案 0 :(得分:5)
Array
有一个初始化程序(init(_:)
)可以从任何Sequence
生成Array
,例如ArraySlice
。但是,使用它会强制使用数组数据的副本,这使得像这样的简单求和算法实际上具有O(nums.count^2)
性能,即使看起来它只扫描数组一次。
func sum(_ nums: [Int]) -> Int {
guard let head = nums.first else { return 0 } //base case, empty list.
return head + sum(Array(nums.dropFirst()))
}
let input = Array(1...10)
let output = sum(input)
print(output)
为了解决这个问题,更好的实现只需在ArraySlice
上运行,允许无副本切片,但这需要首先将输入Array
转换为ArraySlice
。幸运的是,内部函数可以帮助使其对公共API透明,但它确实使代码更长。
func sum(_ nums: [Int]) -> Int {
func sum(_ nums: ArraySlice<Int>) -> Int {
guard let head = nums.first else { return 0 } //base case, empty list.
return head + sum(nums.dropFirst())
}
return sum(ArraySlice(nums))
}
但实际上,正如matt所说,不要这样做。编程的头/尾方法在一种语言中是有意义的,它可以很好地利用模式匹配,良好的编译器优化,尾调用优化等.Swift的设计鼓励使用reduce
。它不仅更短,更易读,而且性能更高。
为了比较,这是一个典型的Swift方法:
extension Sequence where Iterator.Element: Integer {
func sum() -> Iterator.Element {
return self.reduce(0, +)
}
}
Sequence
一起使用,而不仅限于Array
它在任何Integer
类型上都是通用的,而不只是Int
。所以这些都有效:
print(Array<UInt >(1...10).sum())
print(Array<UInt8 >(1...10).sum())
print(Array<UInt16>(1...10).sum())
print(Array<UInt32>(1...10).sum())
print(Array<UInt64>(1...10).sum())
print(Array< Int >(1...10).sum())
print(Array< Int8 >(1...10).sum())
print(Array< Int16>(1...10).sum())
print(Array< Int32>(1...10).sum())
print(Array< Int64>(1...10).sum())
但是,如果你坚持采用这种头/尾方法,你可以尝试以下两种方法之一:
extension Collection {
func headTail1<Head, Tail, ReturnType>(_ closure: (Head?, Tail) -> ReturnType) -> ReturnType
where Head == Self.Element, Tail == Self.SubSequence {
return closure(self.first, self.dropFirst())
}
func headTail2<Head, Tail>() ->(Head?, Tail)
where Head == Self.Element, Tail == Self.SubSequence {
return (self.first, self.dropFirst())
}
}
func sum1<C: Collection, I: Numeric>(_ nums: C) -> I
where C.Element == I {
return nums.headTail1 { head, tail in
guard let head = head else { return 0 } //base case, empty list
return head + sum(tail)
}
}
func sum2<C: Collection, I: Numeric>(_ nums: C) -> I
where C.Element == I {
let (_head, tail) = nums.headTail2()
guard let head = _head else { return 0 } //base case, empty list
return head + sum(tail)
}
print(sum(Array(1...10)))
此代码抽象出列表如何拆分为头尾的详细信息,让您只需担心为您提供的sum
和head
来编写tail
答案 1 :(得分:1)
您的示例存在的问题是,您不会使用head
和tail
来添加数字列表。您拨打reduce
:
let nums = [1,2,3,4,5]
let sum = nums.reduce(0,+)
所以,虽然我喜欢LISP / Scheme作为下一个男人,但是当我们需要 head
和{时,你需要一个更有说服力的案例{1}},因为我们有tail
,map
和filter
(等等)。