在任意操作中快速选择链接?

时间:2014-06-17 19:03:28

标签: ios swift chaining optional

Apple提供了一个简洁的可选链接示例

class Person {
  var residence: Residence?
}

class Residence {
  var numberOfRooms = 1
}

let john = Person()

if let roomCount = john.residence?.numberOfRooms {
  println("John's residence has \(roomCount) room(s).")
} else {
  println("Unable to retrieve the number of rooms.")
}

想象一下尝试用一些算术运算来调整条件。这会导致编译器错误,因为模运算符不支持选项。

if john.residence?.numberOfRooms % 2 == 0 { 
  // compiler error: Value of optional type Int? not unwrapped
  println("John has an even number of rooms")
} else {
  println("John has an odd number of rooms")
}

当然,您总是可以执行以下操作,但它缺乏可选链接的简单性和简洁性。

if let residence = john.residence {
  if residence.numberOfRooms % 2 == 0  {
    println("John has an even number of rooms")
  }else{
    println("John has an odd number of rooms")
  }
} else {
  println("John has an odd number of rooms")
}

是否有任何Swift语言功能可以提供更好的解决方案?

7 个答案:

答案 0 :(得分:3)

我认为你所寻找的通常在函数式编程中被称为monad

它不能直接在swift中使用,但通过使用某些语言功能,您可以自己以通用方式实现monad。 (并且还定义了一个漂亮的中缀运算符,使其看起来像Haskell中的monad)

快速谷歌搜索" monad swift"在https://gist.github.com/cobbal/7562875ab5bfc6f0aed6

找到了一些看起来很有希望的代码

答案 1 :(得分:2)

模式匹配非常强大,可以在以下情况下工作:

switch john.residence?.numberOfRooms {
case .Some(let x) where x % 2 == 0:
    println("Even number of rooms")
default:
    println("Odd number of rooms")
}

答案 2 :(得分:0)

我能想到的最好是Optionals上的扩展名为omap(可选地图)。

extension Optional{
  func omap<K:Any>(optionalBlock:(T)->(K?))->K?{
    if self{
      return optionalBlock(self!)
    }
    return nil
  }
}

与map类似,omap只是将一个可选实例返回给提供的闭包,但是会适当地处理nil大小写。

这允许您使用如下任意操作链接可选项:

if (john.residence.omap{r in r.numberOfRooms % 2 == 0}){
  println("John has an even number of rooms")
} else {
  println("John has an odd number of rooms")
}

请参阅:https://gist.github.com/Grantismo/3e1ba0412e911dfd7cfe

答案 3 :(得分:0)

如果我理解,你想要它说即使它是奇数也可能是奇数,如果它是奇数或任何可选值没有设置。如果是这样的话:

if john.residence?.numberOfRooms && (john.residence!.numberOfRooms! % 2) == 0 {
    println("John has an even number of rooms")
} else {
    println("John has an odd number of rooms")
}
如果没有辅助函数,

是最好的。问题是您需要调用可选值来链接的方法。

当使用可选链接时,if和Bool都有错误,或者我不太了解它,但这对我有用:

extension Int{
    func isEven() -> Bool { return self % 2 == 0 }
}

if (john.residence?.numberOfRooms?.isEven() == true){
    println("John has an even number of rooms")
} else {
    println("John has an odd number of rooms")
}

如果你写

if (john.residence?.numberOfRooms?.isEven())

只要numberOfRooms有一些值,即使isEven()返回false,它也会解析为true。好像表达式是Bool?,但事实并非如此,那就是Bool。

答案 4 :(得分:0)

可能的解决方案是使用Guard

func testOddEven(){
    guard let number = john.residence?.numberOfRooms else {print("Doesn't even have a residence loool"); return }
    switch number % 2 == 0 {
    case true:
        print("event")
        break;
    case false:
        print("odd")
        break;
    }
}

答案 5 :(得分:0)

在过去的10分钟里,我一直在尝试使用可选链接和nil合并运算符来做同样的事情;编译器不喜欢它们中的任何一个。可以先通过tf / then进行操作,然后再使其工作,但感觉很难看。

应该能够对Double进行扩展,尽管我同意它看起来像swift可以(应该?)支持标准运算符中的可选链接。以下是在操场上的作品:

extension Double {
  func divide(by: Double) -> Double {
    return (self/by)
  }
}

var optDouble: Double? = nil
var result = optDouble?.divide(by: 100.0)   // nil

optDouble = 20.25
result = optDouble?.divide(by: 100.0)       // 0.2025

答案 6 :(得分:0)

要使以下行有效(即操作员直接访问可选项后面的值):

if john.residence?.numberOfRooms % 2 == 0

运算符必须位于带有assignment: true的优先级组中。模运算符位于MultiplicationPrecedence优先级组中:

precedencegroup MultiplicationPrecedence {
  associativity:left
  higherThan: AdditionPrecedence
}

您可以使用assignment: true

创建自己的优先群组
precedencegroup AssignmentTruePrecedence {
    assignment: true
    associativity:left
    higherThan:AdditionPrecedence
    lowerThan:MultiplicationPrecedence
}

现在,您可以声明自己的运算符,将其分配给上述优先级组,然后定义关联的函数进行模运算。

infix operator %| : AssignmentTruePrecedence

extension Int {

    static func %| (left: Int, right: Int)->Int {
        return left % right
    }

}

该行将与我们的新模运算符一起正常运行:

if john.residence?.numberOfRooms %| 2 == 0

事实证明,您甚至不必声明新的运算符,因为重新声明模运算符并将其分配给我们的新优先级组似乎可以正常工作!

infix operator % : AssignmentTruePrecedence