【问题标题】:Purpose of typealias类型别名的用途
【发布时间】:2020-08-18 08:43:43
【问题描述】:

我以为今天我终于明白 typealias 是干什么用的了。

我没有。

我们来看一个例子:

typealias Graph = [String: [String]]

let futurama: Graph = [
    "you": ["bender", "hermes", "scruffy"],
    "bender": ["hubert", "zoidberh"],
    "hermes": ["hubert", "amy", "scruffy"],
    "hubert": ["mom", "fry"],
    "fry": ["leela"],
    "leela": ["brannigan", "nibbler", "scruffy"],
    "amy": ["kif"],
    "brannigan": ["kif"],
    "zoidberh": [],
    "kif": [],
    "mom": [],
    "nibbler": [],
    "scruffy": []
]

extension Graph {
    // Breadth First Search
    func bfs(from start: String, to finish: String) -> [String]? { 
        // Implementation of this graph algorithm here
    }
}

print(
    futurama.bfs(from: "you", to: "scruffy")?.joined(separator: " --> ") ?? "There is no pass, sorry"
)

一切都很好。
然后我做了一个小改动:

let futurama: [String: [String]] = [
        "you": ["bender", "hermes", "scruffy"],
        "bender": ["hubert", "zoidberh"],
        ...

我期待现在futurama.bfs() 不会编译,因为futurama 没有方法bfs。我在想,这是多么聪明啊,这门语言的设计真是太棒了!
但我很失望。没有改变。完全没有。代码仍然可以编译和工作。
所以...

  1. typealias 有什么用?
  2. 如何实现我期望的行为?

【问题讨论】:

  • extension Dictionary where Value: Collection, Value.Element: StringProtocol {

标签: swift type-alias


【解决方案1】:

根据文档(在此处找到:https://docs.swift.org/swift-book/ReferenceManual/Declarations.html):

类型别名声明引入了现有类型的命名别名 进入你的程序。

[...]

声明类型别名后,可以使用别名代替 在您的程序中随处可见的现有类型。现有类型可以 是命名类型或复合类型。 类型别名不会创建新的 类型;它们只是允许名称引用现有类型。

所以实际上你并没有定义一个新类型。您只是用另一个名称为您的类型起别名,以使该类型更易于使用。

因此,在您的情况下,Graph 只是类型 [String: [String]] 的另一个别名,不会为您引入新类型。

所以我猜想实现你所期望的有很多可能性。一种方法是包装您的Graph,例如[String: [String]] 到结构或类中,并为该结构/类编写扩展。您实现这一点的方式很大程度上取决于您想要实现的目标。

这能回答你的问题吗?

【讨论】:

    【解决方案2】:
    1. typealias 字面意思是为一个类型创建一个“别名”(即另一个名字)。您不是在创建新类型,而是为现有类型创建另一个 name。来自Language Reference

      类型别名不会创建新类型;它们只是允许名称引用现有类型。

      因此,一旦您声明了类型别名,Graph[String: [String]] 现在就指向同一个类型。 Graph 上的扩展等效于[String: [String]] 上的扩展。

    2. 对于您想要的行为,您需要创建一个新类型。对于Graph,我认为结构是合适的:

      struct Graph {
          ...
      }
      

      Graph 结构然后可以包含(或,封装[String: [String]] 类型的私有属性,

      private var adjacencyDictionary: [String: [String]]
      

      并且您应该编写访问和(可选)改变图形的方法。例如getNode(withName:)getNeighboursOfNode(withName:)addNodeaddEdge

      您不应在此处使用类型别名,因为图表不是[String: [String]]。例如,以下[String: [String]] 不是(有效)图:

      ["a": ["b"]]
      

    【讨论】:

      【解决方案3】:

      typealias 只是语法糖。所有类型别名都会在编译开始时被它们别名的实际类型替换,因此类型别名在类型系统级别上不存在。

      这就是您所看到的行为背后的原因 - 在类型系统级别,Graph 只是 [String:[String]],因此定义使用 Graph 的任何函数实际上都适用于别名类型,即 @ 987654325@.

      如果您希望 Graph 成为真实类型,则需要像这样声明它 - 将其设为 structclass,并带有 Dictionary 的后备存储。

      struct Graph {
          var storage: [String: [String]]
      }
      
      extension Graph {
          // Breadth First Search
          func bfs(from start: String, to finish: String) -> [String]? {
              // Implementation of this graph algorithm here
          }
      }
      
      let futurama: [String: [String]] ...
      futurama.bfs() // This doesn't compile anymore
      

      【讨论】:

        【解决方案4】:

        根据您在下面的回答,我想出了非常巧妙的方法。
        这是下标、ExpressibleByDictionaryLiteral 和泛型的组合。

        struct Graph<T> where T: Hashable {
            private var storage: [T: [T]]
            
            subscript(key: T) -> [T]! {
                storage[key]
            }
            
            func bfs(from start: T, to finish: T) -> [T]? {
                // Implementation of the algorithm here. 
            }
        }
        
        extension Graph: ExpressibleByDictionaryLiteral {
        
            init(dictionaryLiteral elements: (T, [T])...) {
                storage = .init(uniqueKeysWithValues: elements)
            }
        }
        

        我最初帖子中的代码无需任何更改即可编译和工作!
        (除了泛型的东西:String 应该到处改成T

        【讨论】:

          猜你喜欢
          • 2021-08-06
          • 2014-01-10
          • 2011-02-09
          • 2011-04-04
          • 1970-01-01
          • 2020-03-29
          • 2013-10-14
          • 2012-12-09
          • 2010-10-13
          相关资源
          最近更新 更多