我在StackOverflow上遇到了一个问题:Swift - Convert Array to Dictionary 用户想要获取数组中的元素并将其放入字典中,并为每个元素分配0。 (关键是玩家,价值是他们的分数) 所以:
var playerNames = ["Harry", "Ron", "Hermione"]
成为
var scoreBoard: [String:Int] = [ "Ron":0, "Harry":0, "Hermione":0 ]
此问题有2个答案: 1)在数组上使用reduce
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)中运行。
我想知道为什么我们比第一个更喜欢第一个,因为写第二个解决方案的人对他们的编码技能有不好的评价。
编辑:Apple在较新的版本中不赞成使用某些功能,所以坚持基本知识并创建我们自己的处理方式不是更好吗?
谢谢您的回答
答案 0 :(得分:2)
今天,IMO,您不应使用其中任何一个。现在,我们有了Dictionary.init(uniqueKeysWithValues:)
和.init(_:uniquingKeysWith:)
,它们可以更清楚地说明其意图,并明确显示极端情况,例如重复的键。
如果您可以静态证明所有键都是唯一的,则可以使用第一个:
let scoreboard = Dictionary(uniqueKeysWithValues: playerNames.map { (name: $0, score: 0) })
如果您不能证明密钥是唯一的,则可以使用第二个密钥,这样您就可以明确决定在发生冲突时该怎么做。
let scoreboard = Dictionary(playerNames.map { (name: $0, score: 0) },
uniquingKeysWith: { first, _ in first })
请注意此方法如何使标签明确键名和值。我尚未对该代码进行基准测试,但是我希望它在时间上与其他代码非常相似。
答案 1 :(得分:1)
坚持基础知识并创建我们自己的做事方式更好吗?
我不这么认为。 Swift社区当然不是那种心态。 Swift会优先进行有意义的抽象和简化,只要它们是有价值的且逐步公开即可。
围棋社区与您有着相同的见解,但这非常痛苦(IMO)。 Go标准库甚至没有用于反转字符串的API。你必须自己煮。而且这比大多数人想的要难。如果您认为做一个循环来反转字节的简单问题,不对,对于unicode来说这是完全可以解决的(但是在诸如"hello"
之类的简单ASCII测试用例中,它可能不会被注意到。)
类似地,如果您每次想实现for
时都编写map
循环,则可能会忘记调用Array.reserveCapacity(_:)
。忘记该操作将导致多个数组分配,并使看起来O(n)
的算法实际上变成O(n^2)
。这样的性能或正确性“陷阱”很少,因此使用流行的,共享的实现有很大的好处。
我们站在巨人的肩膀上。如果我们全神贯注于重新发明轮子,我们将无法做到这一点。
我不会使用它们中的任何一个
第一种方法:
reduce
),但不适合工作(Dictionary.init(uniqueKeysWithValues:)
)。请参见https://github.com/amomchilov/Blog/blob/master/Don't%20abuse%20reduce.md 第二种方法:
相反,我建议使用Rob Napier或Martin R's approach。两者都更能表达您的意图。 Rob还使用已知大小的序列,该序列允许Dictionary.init(uniqueKeysWithValues:)
在内部为字典分配足够的内存,以适应所有值。
答案 2 :(得分:0)
一般来讲,出于以下几个原因,您应该更喜欢函数式习惯用法(即reduce)而不是for循环:
由于已经过测试,因此默认情况下该功能版本正确。在reduce的情况下,这看似微不足道,但在shuffled
之类的情况下就显得不那么重要了。您如何轻松地查看for循环并告诉我它是否执行了Fisher Yates改组以及该改组是否正确实施?同样,比起一系列顺序的for循环或执行5种不同功能的单个for循环,函数的管道更易于阅读。
功能版本通常是(但不是在这种情况下)不可变,并且不可变值比可变状态更容易推论,因为它们永远不会改变。
Swift.Sequence
上的功能方法在Combine.Publisher
上具有相似的方法。这意味着您可以在所有序列中使用一组功能惯用语,无论它们是同步的还是异步的/反应式的。