【问题标题】:Swift array loop once, write manySwift数组循环一次,写很多
【发布时间】:2015-06-10 15:51:07
【问题描述】:

考虑以下愚蠢的简单示例:

let arr = ["hey", "ho"]
let doubled = arr.map {$0 + $0}
let capitalized = arr.map {$0.capitalizedString}

如您所见,我正在以多种方式处理相同的初始数组,以便最终得到多个处理过的数组。

现在想象arr 很长,并且我有许多这样的进程生成许多最终数组。我不喜欢上面的代码,因为我们循环了多次,每个map 调用一次。我宁愿只循环一次。

现在,显然我们可以通过蛮力来处理这个问题,即从多个可变数组开始并在每次迭代中写入所有数组:

let arr = ["hey", "ho"]
var doubled = [String]()
var capitalized = [String]()
for s in arr {
    doubled.append(s + s)
    capitalized.append(s.capitalizedString)
}

很好。但是现在我们没有得到使用map 的乐趣。所以我的问题是:有没有更好、更快捷的方式?我以一种模糊的方式想象自己使用mapmap 之类的东西来生成类似元组的东西,并在我们迭代时神奇地将元组拆分成所有结果数组,就好像我可以说这样的话(伪代码,不要在家里尝试这个):

let arr = ["hey", "ho"]
let (doubled, capitalized) = arr.map { /* ???? */ }

如果我正在设计自己的语言,我什至可能允许通过赋值到左值伪数组中进行某种喷溅:

let arr = ["hey", "ho"]
let [doubled, capitalized] = arr.map { /* ???? */ }

做不到也没什么大不了,但能这样说话会很有趣。

【问题讨论】:

  • 那么这里的目标仅仅是一个更简洁的语法吗?您的原始代码有 2 个 map 调用,应该比循环一次的效率略低,但在多进程的情况下具有更可并行化的优势,这似乎是可取的。
  • @JackWu 哦,是的,这纯粹是一个句法梦想。我什至不知道我正在做的事情是否特别低效。我没有过早优化,只是盯着我的代码思考。

标签: arrays swift dictionary


【解决方案1】:

一个函数multimap怎么样,它接受一组转换,并应用每个转换,将它们作为数组数组返回:

// yay protocol extensions
extension SequenceType {
    // looks like T->U works OK as a constraint
    func multimap
      <U, C: CollectionType 
       where C.Generator.Element == Generator.Element->U>
    (transformations: C) -> [[U]] {
        return transformations.map {
            self.map($0)
        }
    }
}

然后像这样使用它:

let arr = ["hey", "ho"]

let double: String->String = { $0 + $0 }
let uppercase: String->String = { $0.uppercaseString }

arr.multimap([double, uppercase])
// returns [["heyhey", "hoho"], ["HEY", "HO"]]

或者它可能以可变参数形式非常好:

extension SequenceType {
    func multimap<U>(transformations: (Generator.Element->U)...) -> [[U]] {
        return self.multimap(transformations)
    }
}

arr.multimap({ $0 + $0 }, { $0.uppercaseString })

编辑:如果你想要单独的变量,我认为你能做的最好的就是destructure 函数(不幸的是,你必须为每个 n 元组声明 n 次):

// I don't think this can't be expressed as a protocol extension quite yet
func destructure<C: CollectionType>(source: C) -> (C.Generator.Element,C.Generator.Element) {
    precondition(source.count == 2)
    return (source[source.startIndex],source[source.startIndex.successor()])
}

// and, since it's a function, let's declare pipe forward
// to make it easier to call
infix operator |> { }
func |> <T,U>(lhs: T, rhs: T->U) -> U {
    return rhs(lhs)
}

然后你可以像这样声明变量:

let (doubled,uppercased)
    = arr.multimap({ $0 + $0 }, { $0.uppercaseString }) |> destructure

是的,这有点低效,因为您必须先构建数组,然后再将其拆开——但这实际上并不重要,因为数组是写时复制的,而且我们谈论的是少数它们在外部数组中。

编辑:使用新的guard 声明的借口:

func destructure<C: Sliceable where C.SubSlice.Generator.Element == C.Generator.Element>(source: C) -> (C.Generator.Element,C.Generator.Element) {
    guard let one = source.first else { fatalError("empty source") }
    guard let two = dropFirst(source).first else { fatalError("insufficient elements") }

    return (one,two)
}

【讨论】:

  • 唉,我真的需要以单独的多个一维数组结束。所以我仍然需要另一个循环来将数组数组拆分成这些单独的数组。否则我会用老式的map 做同样的事情不那么整洁...
  • 嗯,好的。是的,你是对的,你需要喷溅。也许 Swift 3.0 将有一个可变的泛型占位符语法,这就是我认为缺少的(例如,将允许您将泛型数组解构写入元组,泛型 zip 等)
  • 我很困惑(并不罕见)。无论涉及多少闭包和最终的一维数组变量,这都会起作用吗? (在现实生活中,大约有十个。)不管闭包产生什么? (在现实生活中,一些目标最终数组是字符串,其他是 Int 等等。)
  • 因此,multimap 将适用于任意数量的闭包。 destructure 你必须为你可能想要创建的每个 n 元组重载 n 次……参见 github.com/typelift/Swiftz/blob/master/Swiftz/Curry.swift 作为这种疯狂的一个例子。但是一旦你这样做了,它就会根据赋值的左侧自动推断要使用哪个重载。
  • 忘记问了,precondition 是什么?我在观看 WWDC 视频时还没有遇到过。
【解决方案2】:

你对元组的建议有什么问题?

let arr = ["hey", "ho"]

let  mapped = arr.map {e in
  return (e + e, e.capitalizedString)
}

【讨论】:

  • 因为那时我需要拆分元组。最后我需要单独的一维数组。想象一条河流分成 5 个分支:我试图将我的原始数组一次变成 5 个数组。 :)
  • 但是你会得到更多的地图乐趣 let doubles = mapped.map { $0.0 } :)
  • 但是我们循环了两次(每个map都是一个循环)。目标是:只循环一次——还记得吗? :)
  • 您的建议并非没有根据,但是拆分元组的第二个循环有点混乱,或多或少不可能一概而论。
【解决方案3】:

这样怎么样,我们在映射“doubled”数组的同时处理“capitalized”数组:

let arr = ["hey", "ho"]

var capitalized = [String]()
let doubled = arr.map {(var myString) -> String in
    capitalized.append(myString.capitalizedString)
    return myString + myString
} 

//doubled ["heyhey", "hoho"]
//capitalized: ["Hey", "Ho"]

【讨论】:

  • 虽然这行得通,但它很糟糕,并且给功能 map 增加了副作用
  • 是的,我同意这一点,我会选择@Airspeed Velocity 的解决方案
猜你喜欢
  • 2016-03-06
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 2021-02-12
  • 2020-11-14
  • 1970-01-01
相关资源
最近更新 更多