我在StackOverflow上遇到一个问题:Swift - Convert Array to Dictionary,用户希望将数组中的元素放入字典中,并为每个元素赋值0。(关键字作为参与者,值作为他们的得分)因此:
var playerNames = ["Harry", "Ron", "Hermione"]
变成了
var scoreBoard: [String:Int] = [ "Ron":0, "Harry":0, "Hermione":0 ]
这个问题得到了2个答案:1)减少阵列上的使用
let scoreboard = playerNames.reduce(into: [String: Int]()) { $0[$1] = 0 }
2)创建字典并对数组进行迭代,以将每个值键对添加到数组中
var dictionary = [String: Int]()
for player in playerNames {
dictionary[player] = 0
}
我用https://github.com/nyisztor/swift-algorithms中的BenchTimer函数来测试这两种求解方法,它们看起来都是O(n)的。
我想知道为什么我们更喜欢第一个而不是另一个,因为写第二个解决方案的人得到了关于他们编码技能的差评。
编辑:一些功能在新版本中被苹果弃用,所以坚持基本功能并创造我们自己的做事方式不是更好吗?
谢谢你的回答
3条答案
按热度按时间dldeef671#
今天,IMO,你不应该使用这两个。我们现在有
Dictionary.init(uniqueKeysWithValues:)
和.init(_:uniquingKeysWith:)
,它们更清楚地说明了它们的意图,并使角落的情况,如重复的键,显式。如果您可以静态地证明所有密钥都是唯一的,那么您将使用第一个密钥:
如果您不能证明这些键是唯一的,则可以使用第二个键,它允许您显式地决定在发生冲突时如何处理。
注意这种方法如何让标签清楚地表明键是什么和值是什么。我还没有对这段代码进行基准测试,但我希望它在时间上与其他代码非常相似。
qxsslcnc2#
所以,坚持基本原则,创造我们自己的做事方式,不是更好吗?
我不这么认为。Swift社区肯定不是这种心态。Swift优先考虑有意义的抽象和简化,只要它们有价值,并逐渐公开。
围棋界和你的想法一样,但这是相当痛苦的Go语言的标准库甚至没有一个API来反转字符串。你必须自己去做。而且这比大多数人想象的要难。如果你认为这是一个简单的问题,做一个循环来反转字节,那就错了。这对于unicode来说是完全不可能的(但是对于
"hello"
之类的简单ASCII测试用例来说可能不会引起注意)。类似地,如果每次要实现
map
时都编写for
循环,则可能会忘记调用Array.reserveCapacity(_:)
。忘记这一点将导致多个数组分配,并使您的O(n)
外观算法实际上成为(摊销)O(n^2)
.像这样的“陷阱”在性能和正确性方面都有很小的缺陷,共享的实现。我们站在巨人的肩膀上。如果我们都专注于重新发明轮子,我们就不可能做到这一点。
关于这两段代码
我都不会用
第一种做法:
1.使用一个函数(
reduce
),但该函数不适用于作业(Dictionary.init(uniqueKeysWithValues:)
)。请参阅https://github.com/amomchilov/Blog/blob/master/Don't%20abuse%20reduce.md1.忘记在字典上保留容量,因此会导致多次调整大小操作(涉及内存重新分配和字典中所有键的重新散列)
第二种做法:
1.使字典不必要地可变
1.还忘记预留容量。
相反,我建议使用Rob Napier或Martin R's approach,它们都更能表达您的意图。Rob的代码还使用了一个已知大小的序列,这允许
Dictionary.init(uniqueKeysWithValues:)
在内部预先为字典分配足够的内存,以适应所有值。anauzrmj3#
一般来说,你应该更喜欢函数式的用法(即reduce)而不是for循环,原因如下:
1.函数式版本传达了意图。在这个例子中,我们将数组归约为字典--这不是最好的例子,但通常通过一些组合函数将向量归约为单个标量。
1.功能版本在默认情况下是正确的,因为它已经过测试了,但是如果你看像
shuffled
这样的东西,情况就不那么严重了。你能很容易地看到一个for循环,然后告诉我它是否执行了Fisher Yates shuffle,以及shuffle是否正确实现了吗?类似地,一个函数流水线比一堆连续的for循环或一个做5件不同事情的for循环更容易阅读。1.函数版本通常(但不是在这种情况下)是不可变的,并且不可变值比可变状态更容易推理,因为它们永远不会改变。
Swift.Sequence
上的函数式方法大多与Combine.Publisher
上的方法类似。这意味着您可以在所有序列上使用一组函数式习语,无论它们是同步的还是异步的/React式的。