swift 通过对数组进行reduce与通过对迭代中的每一项进行赋值来创建字典之间的差异

lvjbypge  于 2022-10-31  发布在  Swift
关注(0)|答案(3)|浏览(146)

我在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)的。

我想知道为什么我们更喜欢第一个而不是另一个,因为写第二个解决方案的人得到了关于他们编码技能的差评。
编辑:一些功能在新版本中被苹果弃用,所以坚持基本功能并创造我们自己的做事方式不是更好吗?
谢谢你的回答

dldeef67

dldeef671#

今天,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 })

注意这种方法如何让标签清楚地表明键是什么和值是什么。我还没有对这段代码进行基准测试,但我希望它在时间上与其他代码非常相似。

qxsslcnc

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.md
1.忘记在字典上保留容量,因此会导致多次调整大小操作(涉及内存重新分配和字典中所有键的重新散列)
第二种做法:
1.使字典不必要地可变
1.还忘记预留容量。
相反,我建议使用Rob NapierMartin R's approach,它们都更能表达您的意图。Rob的代码还使用了一个已知大小的序列,这允许Dictionary.init(uniqueKeysWithValues:)在内部预先为字典分配足够的内存,以适应所有值。

anauzrmj

anauzrmj3#

一般来说,你应该更喜欢函数式的用法(即reduce)而不是for循环,原因如下:
1.函数式版本传达了意图。在这个例子中,我们将数组归约为字典--这不是最好的例子,但通常通过一些组合函数将向量归约为单个标量。
1.功能版本在默认情况下是正确的,因为它已经过测试了,但是如果你看像shuffled这样的东西,情况就不那么严重了。你能很容易地看到一个for循环,然后告诉我它是否执行了Fisher Yates shuffle,以及shuffle是否正确实现了吗?类似地,一个函数流水线比一堆连续的for循环或一个做5件不同事情的for循环更容易阅读。
1.函数版本通常(但不是在这种情况下)是不可变的,并且不可变值比可变状态更容易推理,因为它们永远不会改变。

  1. Swift.Sequence上的函数式方法大多与Combine.Publisher上的方法类似。这意味着您可以在所有序列上使用一组函数式习语,无论它们是同步的还是异步的/React式的。

相关问题