gpt4 book ai didi

swift - 在单个功能指令中用两个值替换 `if/guard let`

转载 作者:搜寻专家 更新时间:2023-11-01 05:52:33 24 4
gpt4 key购买 nike

我正在寻找一种方法来用 中的 functional 对应项替换 if let(或 guard let)测试 swift 。我只是找不到办法去做,也许这是不可能的,但我对此很好奇。

所以我有这段代码:

struct Example {
var first: String? = "First"
var second: String? = "Second"

var concat: String? {
guard let first = first, let second = second else {
return nil
}

return first + second
}
}

var example = Example()
example.concat // FirstSecond

example.first = nil
example.concat // nil

我想要的是 concat 变量是 nil 如果 first 或 second 中的任何一个是 nil。这就是上面的代码所做的。我在考虑如何让它功能性 *rainbows*。

我想到了这个,但我并不满意,因为我的 flatMap 需要显式参数名称,我想知道是否有更简洁的解决方案(暗示 map 链接或类似的东西):

struct Example {
var first: String? = "First"
var second: String? = "Second"

var concat: String? {
return first.flatMap { f in second.map { s in f + s } }
}
}

var example = Example()
example.concat // FirstSecond

example.first = nil
example.concat // nil

我考虑过元组,但它总是意味着使用像 Array 这样的中间类型,我觉得这不是很干净。

编辑 1: 最后,请考虑到此示例使用的是 String,但我想找到一个适用于任何 Optional 的解决方案 类型。

我的理想解决方案是这样的:

var concat: String? {
return (first, second).map { $0 + $1 }
}

但这是不可能的,因为我们不能为元组定义函数。

编辑 2: 我得到的最接近(从我的理想解决方案)的代码是定义一个全局函数(不是很干净),如下所示:

func opMap<T>(_ tuple: (T?, T?), transform: (T,T) -> T?) -> T? {
guard let one = tuple.0, let two = tuple.1 else { return nil }
return transform(one, two)
}

var concat: String? {
return opMap((first, second)) { $0 + $1 }
}

另一个限制出现在这里,我只能使用成对的值。我的理想解决方案也适用于任意数量的值。

编辑 3:正如@Hamish 在评论中提出的那样,这是一种使用可选 扩展来解决问题的有趣方法:

extension Optional {
func map(with: Wrapped?, transform: (Wrapped, Wrapped) -> Wrapped?) -> Wrapped? {
guard case .some(let first) = self, let second = with else { return nil }
return transform(first, second)
}
}

var concat: String? {
return first.map(with: second) { $0 + $1 }
}

编辑 4:(是的,我喜欢编辑)@Martin R 提出了对以前解决方案的改进,它更接近我想要的(因为我们有独立的第一、第二和结果类型) ,见下文:

extension Optional {
func map<T, S>(with: T?, transform: (Wrapped, T) -> S?) -> S? {
guard case .some(let first) = self, let second = with else { return nil }
return transform(first, second)
}
}

上次编辑正确:我强烈建议您查看@Rob Napier 的回答及其评论,其中对我要实现的目标提出了很多要点。

最佳答案

虽然我认为原始代码是非常好的 Swift,以及你应该使用的东西(并且它没有任何功能),但你为此所需的 FP 工具在 Haskell 中称为 sequence。您在这里构建的是一个 monad(我知道“monad”总是令人困惑的对话的开始,但这正是您正在构建的)。您需要一个表达式,如果所有元素都有值,则表达式有值,但如果任何元素没有值,则表达式没有值。这正是 monad 非常常用的用途。

那么让我们构建序列。不幸的是,这与 Swift 的 sequence 冲突,但我真的不建议为此目的使用此工具。 if-let 是 Swift 中更强大的 monad;事实上,它非常接近 Haskell 的 do-notation。

// I'd never build `sequence` this way. I'd build it with a for-loop in Swift, but 
// to stay super functional...
func sequence<T>(_ elements: [T?]) -> [T]? {
let result = elements.flatMap{$0}
return result.count == elements.count ? result : nil
}

好的,我们要用它做什么?好吧,这一切都非常笼统(就像好的 FP 一样)。它应该对两个元素和一百个元素同样有效。所以数组正是工具。 reduce 让我们把一个数组变成一个值:

var concat: String? {
return sequence([first, second])?.reduce("", +)
}

关于swift - 在单个功能指令中用两个值替换 `if/guard let`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43762627/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com