使用数组内容时Swift nil合并

时间:2014-08-27 19:24:20

标签: arrays swift null optional

我只是想知道是否有更可读/更简洁的方式来编写以下内容:

let rankMap: Dictionary<Int,String> = [1:"Ace", 11:"Jack", 12:"Queen", 13:"King"]

func getCardImage(suit: String, rank: Int) -> NSImage {
  var imgRank: String
  if rankMap[rank] == nil {
    imgRank = rank.description
  } else {
    imgRank = (rankMap[rank]! as NSString).substringToIndex(1)
  }   
  let imgSuit = (suit as NSString).substringToIndex(1)
  //let imgRank: String = rankMap[rank] ?? rank.description

  return NSImage(named: imgRank + imgSuit)
}

特别是,我对检查rankMap的部分和nil感兴趣。无论从阵列返回什么,除了第一个被切断之外,每个角色都需要。我不能写这样的东西:

let imgRank: String = (rankMap[rank] as NSString).substringToIndex(1) ?? rank.description

..由于nil合并的语义。除了做“if nil”检查的“艰难方式”之外,还有什么我可以做的吗?

2 个答案:

答案 0 :(得分:2)

我删除了NSImage部分,只是因为它更容易作为文件名进行测试,但这应该表现得相似。

func cardImage(suit: String, rank: Int) -> String {
  let imgRankFull = rankMap[rank] ?? rank.description
  let imgRank = first(imgRankFull) ?? " "

  let imgSuit = first(suit) ?? " "

  return imgRank + imgSuit
}

这显示了如何使用??,但它仍然有很多小问题IMO。我建议您使用枚举替换suitrank。目前的方案有很多方法可以导致错误,而且你没有做任何好的错误检查。我已经躲过了回归&#34; &#34;,但实际上这些应该是fatalError或可选项,或Result(带有值或错误的对象)。

答案 1 :(得分:0)

您不需要substringToIndex来获取Swift字符串的第一个字符。 StringCollectionType,标准库中的此通用函数适用于该文件:

func first<C : CollectionType>(x: C) -> C.Generator.Element?

所以你可以从尝试这样的事情开始:

let imgRank = first(rankMap[rank]) ?? rank.description // error

这不起作用,原因有两个:

  • String.Generator.ElementCharacter,而非String,因此,如果您要将其放在??表达式中,则为{{1}的右操作数1}}也需要是??。您可以从CharacterCharacter)构建String,但如果该字符串中包含多个字符,则会崩溃。
  • Character(rank.description)是可选的,因此您无法将其直接传递给rankMap[rank]。但是如果你试图立即强行解开它(使用first,你会在查找不在字典中的东西时崩溃。

您可以尝试将first(rankMap[rank]!)置于??调用内,同时解决这些问题,但您可能需要一个两个字符的字符串作为等级10。

如果我们可以将first视为String,而Array提供var first来获取其第一个元素,那就太好了。 (对于返回first的字符串,有一个String版本。)然后我们可以利用可选链接将整个表达式放在??的左侧,然后写:

let imgRank = rankMap[rank]?.first ?? rank.description

好吧,让我们把它写在扩展名中!

extension String {
    var first : String? {
        // we're overloading the name of the global first() function,
        // so access it with its module name
        switch Swift.first(self) {
        case let .Some(char):
            return String(char)
        case .None:
            return nil
        }
    }
}

现在你可以很简洁:

let imgRank = rankMap[rank]?.first ?? String(rank)

(注意我也将rank.description更改为String(rank)。明确表示好 - description为您提供值的字符串表示&# 39; s适合在调试器中打印; String()将值转换为字符串。前者不能保证始终与后者匹配,因此如果您真正想要的话,请使用后者。)< / p>


无论如何,@ RobNapier的建议表明:你最好不要使用枚举。这样可以减少关于选项和无效查找的不确定性。但是看看你能用标准库做些什么总是很有用的。