说明

Array,Dictionary并且Set是Swift标准库中捆绑的常用集合类型。但是,如果他们没有立即提供您的应用所需的一切,该怎么办?别担心。您可以使用Swift标准库中的协议创建自己的自定义集合!

Swift中的集合带有大量方便的实用程序,可用于遍历它们,对其进行过滤等。除了使用自定义集合之外,您还可以将所有业务逻辑添加到自己的代码中。但是,这会使您的代码肿,难以维护并且无法复制标准库提供的内容。

幸运的是,Swift提供了功能强大的收集协议,因此您可以创建自己的收集类型,这些收集类型专门为满足应用程序的需求而量身定制。您只需实现这些协议即可带来Swift集合的强大功能。

在本教程中,您将从头开始构建一个multiset,也称为bag。

在此过程中,您将学习如何:

  • 采用这些协议:Hashable,Sequence,Collection,CustomStringConvertible,ExpressibleByArrayLiteral和ExpressibleByDictionaryLiteral。
  • 为您的集合创建自定义初始化。
  • 使用自定义方法改进自定义集合。

是时候跳进去了!

1. 实战

1.1 创建 Bag.playground

接下来,将以下代码添加到您的游乐场:

struct Bag<Element: Hashable> {}

就像这样,“爸爸有了一个全新的包”!

您Bag是需要Hashable元素类型的通用结构。通过要求Hashable元素,您可以比较和存储O(1)时间复杂度的唯一值。这意味着无论其内容的大小如何,Bag都将以恒定的速度执行。另外,请注意,您正在使用struct;; 就像Swift对标准集合所做的那样,这会强制执行值语义。

ABag之所以像a Set,是因为它不存储重复的值。不同之处在于:ABag保留任何重复值的连续计数,而aSet则不保留。

像购物清单一样考虑它。如果您想要多个,则不要多次列出。您只需在项目旁边写上您想要的号码。

要对此建模,请Bag在您的游乐场中添加以下属性:

// 1
fileprivate var contents: [Element: Int] = [:]// 2
var uniqueCount: Int {return contents.count
}// 3
var totalCount: Int {return contents.values.reduce(0) { $0 + $1 }
}

这些是所需的基本属性Bag。这是每个人的工作:

  1. contents:使用aDictionary作为内部数据结构。这对于a非常Bag有用,因为它强制执行用于存储元素的唯一键。每个元素的值就是其计数。请注意,您将此属性标记为fileprivate对Bag外部隐藏其内部工作。
  2. uniqueCount:返回唯一项的数量,忽略其单独数量。例如,一个Bag包含4个橙子和3个苹果的a将返回uniqueCount2。
  3. totalCount:返回中的项目总数Bag。在上面的示例中,totalCount将返回7。

1.2 添加编辑方法

现在,您将实现一些方法来编辑的内容Bag。

添加添加方法
在刚添加的属性下面添加以下方法:

// 1
mutating func add(_ member: Element, occurrences: Int = 1) {// 2precondition(occurrences > 0,"Can only add a positive number of occurrences")// 3if let currentCount = contents[member] {contents[member] = currentCount + occurrences} else {contents[member] = occurrences}
}

这是这样做的:

  1. add(_:occurrences :):提供一种向中添加元素的方法Bag。它具有两个参数:通用类型Element,和可选的出现次数。将方法标记为,mutating以便可以修改contents实例变量。
  2. precondition(_:_ :):需要大于0的出现。如果此条件为假,则执行停止,String并且遵循该条件的将会出现在游乐场调试器中。
    本部分检查包装袋中是否已存在该元素。如果是这样,它将增加计数。如果没有,它将创建一个新元素。

注意:您将precondition在本教程中通篇使用,以确保Bag按预期使用。您还将precondition用作健全性检查,以确保添加功能时一切正常。逐步执行此操作将使您避免意外破坏以前运行的功能。

现在您已经可以将元素添加到Bag实例中,还需要一种方法来删除它们。

1.3 实现删除方法

在下面添加以下方法add(_:occurrences:):

mutating func remove(_ member: Element, occurrences: Int = 1) {// 1guard let currentCount = contents[member],currentCount >= occurrences else {return}// 2precondition(occurrences > 0,"Can only remove a positive number of occurrences")// 3if currentCount > occurrences {contents[member] = currentCount - occurrences} else {contents.removeValue(forKey: member)}
}

请注意,remove(:occurrences:)其参数与相同add(:occurrences:)。运作方式如下:

  1. 首先,它检查该元素是否存在,以及它至少具有调用者要删除的出现次数。如果不是,则该方法返回。
  2. 接下来,确保要删除的出现次数大于0。
  3. 最后,它检查元素的当前计数是否大于要删除的出现次数。如果更大,则通过从当前计数中减去要删除的出现次数来设置元素的新计数。如果不大于,则currentCount和出现次数相等,它将完全删除该元素。

现在Bag做不了什么。您无法访问其内容,也无法使用任何有用的收集方法(例如map,等)对您的集合进行操作filter。

但是,一切都不会丢失!Swift提供了制作Bag合法集合所需的工具。您只需要遵循一些协议即可。

1.4 采用协议

在Swift中,协议定义了一组属性和方法,这些属性和方法必须在采用它的对象中实现。要采用协议,只需在您的定义之后添加一个冒号,class或struct后跟您要采用的协议名称。声明采用协议后,请在对象上实现所需的变量和方法。完成后,您的对象将符合协议。

注意:您可以在我们的面向协议的编程教程中了解有关协议的更多信息。

这是一个简单的例子。当前,Bag对象在Playground的结果侧栏中几乎没有显示任何信息。

将以下代码添加到运动场的末尾(结构外部)以Bag进行操作:

var shoppingCart = Bag<String>()
shoppingCart.add("Banana")
shoppingCart.add("Orange", occurrences: 2)
shoppingCart.add("Banana")
shoppingCart.remove("Orange")

然后按Command-Shift-Enter执行操场。

这将创建一个Bag带有少量水果的新产品。如果您查看运动场调试器,则会看到对象类型,但不包含任何内容。

1.5 采用CustomStringConvertible

幸运的是,Swift为CustomStringConvertible这种情况提供了协议!在的大括号后添加以下内容Bag:

extension Bag: CustomStringConvertible {var description: String {return String(describing: contents)}
}

符合CustomStringConvertible要求实现名为的单个属性description。此属性返回特定实例的文本表示形式。

您将在此处放置创建代表数据的字符串所需的任何逻辑。因为Dictionary符合CustomStringConvertible,您只需将description调用委派给即可contents。

按Command-Shift-Enter再次运行游乐场。

看一下新改进的调试信息shoppingCart:

太棒了!现在,在向中添加功能时Bag,您将能够验证其内容。

大!创建功能强大的原生集合类型时,您就在途中。接下来是初始化。

1.6 创建初始化器

非常烦人的是,您必须一次添加一个元素。您应该能够Bag通过传递要添加的对象集合来初始化自己。

将以下代码添加到运动场的末尾(但请注意,这尚不能编译):

let dataArray = ["Banana", "Orange", "Banana"]
let dataDictionary = ["Banana": 2, "Orange": 1]
let dataSet: Set = ["Banana", "Orange", "Banana"]var arrayBag = Bag(dataArray)
precondition(arrayBag.contents == dataDictionary,"Expected arrayBag contents to match \(dataDictionary)")var dictionaryBag = Bag(dataDictionary)
precondition(dictionaryBag.contents == dataDictionary,"Expected dictionaryBag contents to match \(dataDictionary)")var setBag = Bag(dataSet)
precondition(setBag.contents == ["Banana": 1, "Orange": 1],"Expected setBag contents to match \(["Banana": 1, "Orange": 1])")

您可能会期望这样创建一个Bag。但是它不会编译,因为您尚未定义接受其他集合的初始化程序。无需为每种类型显式创建初始化方法,而是使用generics。

totalCount在实现的下面添加以下方法Bag:

// 1
init() { }// 2
init<S: Sequence>(_ sequence: S) whereS.Iterator.Element == Element {for element in sequence {add(element)}
}// 3
init<S: Sequence>(_ sequence: S) whereS.Iterator.Element == (key: Element, value: Int) {for (element, count) in sequence {add(element, occurrences: count)}
}

这是您刚刚添加的内容:

  1. 首先,您创建了一个空的初始化程序。定义其他init方法时,需要添加此内容。
  2. 接下来,您添加了一个初始化程序,该初始化程序接受符合Sequence协议的所有内容,其中该序列的元素与的元素相同Bag。这涵盖了Array和Set类型。您遍历顺序传递的内容,并一次添加每个元素。
  3. 此后,您添加了一个类似的初始化程序,但是它接受type的元组(Element, Int)。一个例子是Dictionary。在这里,您遍历序列中的每个元素并添加指定的计数。

再次按Command-Shift-Enter即可运行游乐场。请注意,您之前在底部添加的代码现在可以使用。

1.7 初始化集合

这些通用的初始化程序为Bag对象提供了更多种类的数据源。但是,它们确实需要您首先创建传递给初始化程序的集合。

为了避免这种情况,Swift提供了两种协议来启用序列文字的初始化。文字为您提供了一种无需显式创建对象即可写数据的简便方法。

为此,请首先将以下代码添加到游乐场的末尾:(注意:在添加所需的协议之前,这也会产生错误。)

var arrayLiteralBag: Bag = ["Banana", "Orange", "Banana"]
precondition(arrayLiteralBag.contents == dataDictionary,"Expected arrayLiteralBag contents to match \(dataDictionary)")var dictionaryLiteralBag: Bag = ["Banana": 2, "Orange": 1]
precondition(dictionaryLiteralBag.contents == dataDictionary,"Expected dictionaryLiteralBag contents to match \(dataDictionary)")

上面的代码是使用Array和Dictionary文字而不是对象进行初始化的示例。

现在,要使它们起作用,请在扩展名下面添加以下两个扩展CustomStringConvertible名:

// 1
extension Bag: ExpressibleByArrayLiteral {init(arrayLiteral elements: Element...) {self.init(elements)}
}// 2
extension Bag: ExpressibleByDictionaryLiteral {init(dictionaryLiteral elements: (Element, Int)...) {self.init(elements.map { (key: $0.0, value: $0.1) })}
}
  1. ExpressibleByArrayLiteral用于根据Bag数组样式文字创建一个。在这里,您可以使用之前创建的初始化程序,并传入elements集合。
  2. ExpressibleByDictionaryLiteral除了字典样式文字外,其功能相同。该映射将元素转换为初始化程序期望的命名元组。

随着Bag看起来更像一个原生的集合类型,它的时间去真正的魔力。

2. 了解自定义集合

您现在已经学到了足够的知识,可以理解什么是自定义集合:您定义的集合对象同时符合Sequence和Collection协议。

在上一节中,您定义了一个初始化程序,该初始化程序接受符合Sequence协议的集合对象。Sequence表示一种类型,该类型提供对其元素的顺序,迭代访问。您可以将序列视为一系列项目,让您一次遍历每个元素。

迭代是一个简单的概念,但是此功能为您的对象提供了巨大的功能。它允许您执行各种强大的操作,例如:

  1. map(_ :):使用提供的闭包转换序列中的每个元素后,返回结果数组。
  2. filter(_ :):返回满足提供的闭包谓词的元素数组。
  3. sorted(by :):返回基于提供的闭包谓词排序的元素数组。

上面是常用的方法。要查看所有可用的方法Sequence,请查看Apple的Sequence Protocol文档。

2.1 加强无损迭代

一个警告:Sequence不需要符合性的类型是非破坏性的。这意味着在迭代之后,无法保证将来的迭代将从头开始。如果您计划多次迭代数据,那将是一个巨大的问题。

要执行无损迭代,您的对象需要符合Collection协议。

Collection继承自Indexable和Sequence。

主要区别在于,集合是可以多次遍历并按索引访问的序列。

遵循,您将免费获得许多方法和属性Collection。一些例子是:

  1. isEmpty:返回一个布尔值,指示集合是否为空。
  2. first:返回集合中的第一个元素。
  3. count:返回集合中元素的数量。

根据集合中元素的类型,还有更多可用的方法。在Apple的“收集协议”文档中进行检查。

抓住Bag并采用这些协议!

2.2 采用Sequence Protocol序列协议

对集合类型执行的最常见操作是遍历其元素。例如,将以下内容添加到游乐场的末尾:

for element in shoppingCart {print(element)
}

与Array和一样Dictionary,您应该可以循环浏览Bag。这将无法编译,因为当前Bag类型不符合Sequence。

现在修复该问题。

2.3 符合Sequence Protocol

在ExpressibleByDictionaryLiteral扩展名后添加以下内容:

extension Bag: Sequence {// 1typealias Iterator = DictionaryIterator<Element, Int>// 2func makeIterator() -> Iterator {// 3return contents.makeIterator()}
}

不需要太多的钱去遵守Sequence。在上面的代码中,您:

  1. 创建一个typealias命名Iterator为DictionaryIterator。Sequence要求它知道如何迭代序列。DictionaryIterator是Dictionary对象用来遍历其元素的类型。您使用此类型是因为Bag将其基础数据存储在中Dictionary。
  2. 定义makeIterator()为返回Iterator用于逐步浏览序列中每个元素的方法。
  3. 通过委派给makeIterator()on来返回迭代器,该迭代器contents本身符合Sequence。

这就是您需要Bag遵循的所有条件Sequence!

您现在可以遍历a的每个元素Bag并获取每个对象的计数。在上一个for-in循环之后,将以下内容添加到运动场的末尾:

for (element, count) in shoppingCart {print("Element: \(element), Count: \(count)")
}

按Command-Shift-Enter运行操场。打开操场上的控制台,您将在顺序中看到元素的打印输出及其数量。

2.4 查看序列的好处

能够通过进行迭代,Bag可以实现由实现的许多有用方法Sequence。将以下内容添加到操场的末端,以查看其中的一些操作:

// Find all elements with a count greater than 1
let moreThanOne = shoppingCart.filter { $0.1 > 1 }
moreThanOne
precondition(moreThanOne.first!.key == "Banana" && moreThanOne.first!.value == 2,"Expected moreThanOne contents to be [(\"Banana\", 2)]")// Get an array of all elements without their counts
let itemList = shoppingCart.map { $0.0 }
itemList
precondition(itemList == ["Orange", "Banana"] ||itemList == ["Banana", "Orange"],"Expected itemList contents to be [\"Orange\", \"Banana\"] or [\"Banana\", \"Orange\"]")// Get the total number of items in the bag
let numberOfItems = shoppingCart.reduce(0) { $0 + $1.1 }
numberOfItems
precondition(numberOfItems == 3,"Expected numberOfItems contents to be 3")// Get a sorted array of elements by their count in descending order
let sorted = shoppingCart.sorted { $0.0 < $1.0 }
sorted
precondition(sorted.first!.key == "Banana" && moreThanOne.first!.value == 2,"Expected sorted contents to be [(\"Banana\", 2), (\"Orange\", 1)]")

按Command-Shift-Enter键可以运行游乐场并查看它们的运行情况。

这些都是处理序列的有用方法-您实际上是免费获得的!

现在,您可以满足于事物的处理方式Bag,但是这样做的乐趣在哪里?您绝对可以改善当前的Sequence实现。

2.5 改善Sequence

当前,您依靠它Dictionary来为您处理繁重的工作。很好,因为这样可以轻松创建自己的强大集合。问题在于它为Bag用户带来了奇怪而令人困惑的情况。例如,Bag返回type的迭代器并不直观DictionaryIterator。

但是,斯威夫特再次来了!Swift提供了一种类型,AnyIterator可以将基本的迭代器从外界隐藏起来。

用Sequence以下替换扩展的实现:

extension Bag: Sequence {// 1typealias Iterator = AnyIterator<(element: Element, count: Int)>func makeIterator() -> Iterator {// 2var iterator = contents.makeIterator()// 3return AnyIterator {return iterator.next()}}
}

在此修订的Sequence扩展中,您:

  1. 定义Iterator为符合AnyIterator而不是DictionaryIterator。然后,像以前一样,创建makeIterator()返回Iterator。
  2. 创建iterator调用makeIterator()上contents。下一步需要此变量。
  3. 包装iterator一个新AnyIterator对象以转发其next()方法。该next()方法是在迭代器上调用的,以获取序列中的下一个对象。

按Command-Shift-Enter运行操场。您会注意到几个错误:

之前,你用DictionaryIterator用的元组的名称key和value。您已DictionaryIterator远离外界,并将暴露的元组名称重命名为element和count。

要修复错误,请更换key和value使用element,并count分别。立即运行游乐场,您的precondition积木将像以前一样经过。

现在没有人会知道您只是Dictionary在为自己辛苦工作!

是时候带您Bag回家了。好,好,收集您的兴奋,是Collection时候了!:]

2.6 通过Collection协议

事不宜迟,这里是创建集合的真正内容:Collection协议!重申一下,aCollection是可以按索引访问并遍历多次的序列。

要采用Collection,您需要提供以下详细信息:

  1. startIndexendIndex:定义一个集合的边界并公开横向的起点。
  2. 下标(position :):允许使用索引访问集合中的任何元素。此访问应以O(1)时间复杂度运行。
  3. index(after :):在传入索引之后立即返回索引。
    拥有有效的收藏集仅需四个细节。你懂的 它在Bag!

在Sequence扩展名之后添加以下代码:

extension Bag: Collection {// 1typealias Index = DictionaryIndex<Element, Int>// 2var startIndex: Index {return contents.startIndex}var endIndex: Index {return contents.endIndex}// 3subscript (position: Index) -> Iterator.Element {precondition(indices.contains(position), "out of bounds")let dictionaryElement = contents[position]return (element: dictionaryElement.key,count: dictionaryElement.value)}// 4func index(after i: Index) -> Index {return contents.index(after: i)}
}

这很简单。在这里,您:

  1. 声明Index中定义的类型Collection为DictionaryIndex。您会将这些索引传递给contents。
  2. 从返回索引的开始和结束contents。
  3. 使用precondition来强制执行有效索引。您将从contents该索引处返回的值作为新的元组。
  4. 返回index(after:)call on的值contents。

通过简单地添加这些属性和方法,您就创建了一个功能齐全的Collection!

2.7 测试Collection

将以下代码添加到游乐场的末尾以测试一些新功能:

// Get the first item in the bag
let firstItem = shoppingCart.first
precondition((firstItem!.element == "Orange" && firstItem!.count == 1) ||(firstItem?.element == "Banana" && firstItem?.count == 2),"Expected first item of shopping cart to be (\"Orange\", 1) or (\"Banana\", 2)")// Check if the bag is empty
let isEmpty = shoppingCart.isEmpty
precondition(isEmpty == false,"Expected shopping cart to not be empty")// Get the number of unique items in the bag
let uniqueItems = shoppingCart.count
precondition(uniqueItems == 2,"Expected shoppingCart to have 2 unique items")// Find the first item with an element of "Banana"
let bananaIndex = shoppingCart.indices.first { shoppingCart[$0].element == "Banana"
}!
let banana = shoppingCart[bananaIndex]
precondition(banana.element == "Banana" && banana.count == 2,"Expected banana to have value (\"Banana\", 2)")

再次运行游乐场。太棒了!

提示一下您对所做的事情感到非常满意的那一刻,但感觉到即将出现“但请稍等,您可以做得更好”的评论……嗯,您是对的!您可以做得更好。仍然有一些Dictionary气味从您的漏出Bag。

2.8 改善Collection

Bag回到过多的内部运作。用户Bag需要使用DictionaryIndex对象集合中元素的访问。

您可以轻松解决此问题。在Collection扩展名后面添加以下内容:

// 1
struct BagIndex<Element: Hashable> {// 2fileprivate let index: DictionaryIndex<Element, Int>// 3fileprivate init(_ dictionaryIndex: DictionaryIndex<Element, Int>) {self.index = dictionaryIndex}
}

在上面的代码中,您:

  1. 定义一个新的通用类型BagIndex。像一样Bag,这需要Hashable用于字典的泛型类型。
  2. 使该索引类型的基础数据成为DictionaryIndex对象。BagIndex实际上只是一个包装,将其真实的索引对外界隐藏。
  3. 创建一个接受DictionaryIndex存储的初始化程序。

现在,您需要考虑以下事实:Collection必须Index具有可比性才能比较两个索引来执行操作。因此,BagIndex需要采用Comparable。

在以下位置添加以下扩展名BagIndex:

extension BagIndex: Comparable {static func ==(lhs: BagIndex, rhs: BagIndex) -> Bool {return lhs.index == rhs.index}static func <(lhs: BagIndex, rhs: BagIndex) -> Bool {return lhs.index < rhs.index}
}

这里的逻辑很简单;您正在使用的等效方法DictionaryIndex来返回正确的值。

2.9 更新BagIndex

现在您可以更新Bag使用了BagIndex。用Collection以下内容替换扩展名:

extension Bag: Collection {// 1typealias Index = BagIndex<Element>var startIndex: Index {// 2.1return BagIndex(contents.startIndex)}var endIndex: Index {// 2.2return BagIndex(contents.endIndex)}subscript (position: Index) -> Iterator.Element {precondition((startIndex ..< endIndex).contains(position),"out of bounds")// 3let dictionaryElement = contents[position.index]return (element: dictionaryElement.key,count: dictionaryElement.value)}func index(after i: Index) -> Index {// 4return Index(contents.index(after: i.index))}
}

每个编号的注释都表示更改。它们是:

  1. 将Index类型从替换DictionaryIndex为BagIndex。
  2. 创建一个新的BagIndex从contents两个startIndex和endIndex。
  3. 使用的index属性BagIndex访问并从中返回元素contents。
  4. 使用的属性获取DictionaryIndex值contents,BagIndex并BagIndex使用此值创建新的值。

而已!用户回到对存储数据的方式一无所知。您还可能会更好地控制索引对象。

在总结之前,还有一个更重要的主题需要讨论。通过添加基于索引的访问,您现在可以为集合中的一系列值建立索引。是时候让您看看分片如何与集合一起工作了。

3 使用切片Slices

切片是视图集合中元素的子序列。它使您无需复制就可以对元素的特定子序列执行操作。

切片存储对创建它的基础集合的引用。切片与它们的基本集合共享索引,保留对开始和结束索引的引用以标记子序列范围。切片具有O(1)空间复杂度,因为它们直接引用其基本集合。

要查看其工作原理,请将以下代码添加到游乐场的末尾:

// 1
let fruitBasket = Bag(dictionaryLiteral:("Apple", 5), ("Orange", 2), ("Pear", 3), ("Banana", 7))// 2
let fruitSlice = fruitBasket.dropFirst()// 3
if let fruitMinIndex = fruitSlice.indices.min(by:{ fruitSlice[$0] > fruitSlice[$1] }) {// 4let basketElement = fruitBasket[fruitMinIndex]let sliceElement = fruitSlice[fruitMinIndex]precondition(basketElement == sliceElement,"Expected basketElement and sliceElement to be the same element")
}

再次运行游乐场。

在上面的代码中,您:

  1. 创建一个由四个不同水果组成的水果篮。
  2. 取出第一种水果。实际上,这只是在水果篮中创建一个新的切片视图(不包括您删除的第一个元素),而不是创建一个全新的Bag对象。您会在结果栏中注意到这里的类型是Slice<Bag>。
  3. 在剩余的水果中找到最少出现的水果的索引。
  4. 证明即使从切片中计算索引,您也可以使用基础集合和切片中的索引来检索相同的元素。

注意:对于像这样的基于哈希的集合,切片似乎没什么用Dictionary,Bag因为切片的顺序没有以任何有意义的方式定义。Array另一方面,An是集合类型的一个很好的例子,其中切片在执行子序列操作中起着巨大的作用。

恭喜!您现在是Collection专家!您可以通过填写Bag自己的自定义奖品来庆祝。:]

4 然后去哪儿?

您可以使用本教程顶部或底部的“下载资料”按钮,下载包含本教程中所有代码的完整游乐场。

在本教程中,您学习了如何在Swift中创建自定义集合。您为Sequence,Collection,CustomStringConvertible,ExpressibleByArrayLiteral,ExpressibleByDictionaryLiteral添加了一致性,并创建了自己的索引类型。

如果您想查看或更完整的Bag实现,请查看Swift Algorithm Club实现以及Foundation实现NSCountedSet。

这些只是Swift提供的用于创建健壮且有用的集合类型的所有协议的一种体验。如果您想了解一些此处未涵盖的内容,请查看以下内容:

  • 数组关系
  • 字典关系
  • 双向集合关系

您还可以查看有关Swift中协议的更多信息,并了解更多有关采用Swift标准库中可用的通用协议的信息。

最后,请务必阅读有关Swift中面向协议编程的文章!

参考

https://www.raywenderlich.com/10286147-building-a-custom-collection-with-protocols-in-swift

翻译:protocol的高阶用法,在Swift 5中使用协议protocol构建自定义集合Collection相关推荐

  1. 【性能测试】如何用一条命令完全掌握linux系统性能监控(top高阶用法)

    目  录 一 引 言 二 top命令高阶用法 场景1:采样3次,采样间隔为10s: 场景2:采样2h,采样间隔为10s,性能数据保存到test.csv文件中: 一 引 言 熟悉CentOS linux ...

  2. ifdef的用法_chisel 高阶用法简介--rocket-chip generator

    本文将介绍chisel的三个高阶用法:diplomacy,cake pattern和参数化. diplomacy 什么是diplomacy?互联参数的自动协商. 痛点在哪里: 传统的SoC集成中,互联 ...

  3. Peewee 高阶用法

    Peewee 高阶用法 前言 本文介绍的Peewee方法语法基于PostgreSQL 高阶用法 元组多条件查询 from peewee import Tuple e.g.: 1. model.sele ...

  4. python mockito arg_that_编程高阶用法–开发者高频词汇

    开发者总会在开发时遇到变量命名困难或者命名冗长庸俗的时候. 阅读代码过程中遇到一些很好的命名,也遇到一些不好的. 当初并没有记录下来,之后才开始记录,有的也找不到出处了.以下高频词汇供有追求的开发者参 ...

  5. React之ref的高阶用法

    forwardRef转发Ref forwardRef的初衷就是解决ref不能跨层级捕获和传递的问题,forwardRef接受了父级元素标记的ref信息,并把它转发下去,使得子组件可以通过props来接 ...

  6. python什么是高阶函数_说说 Python 中的高阶函数

    高阶函数(higher-order function)指的是:接受一个函数为参数,或者把函数作为结果值返回的函数1. 1 sorted() 比较常见的高阶函数是 sorted(),其内部的关键字参数 ...

  7. day67 ORM模型之高阶用法整理,聚合,分组查询以及F和Q用法,附练习题整理

    归纳总结的笔记: day67ORM特殊的语法一个简单的语法 --翻译成--> SQL语句语法:1. 操作数据库表 创建表.删除表.修改表2. 操作数据库行 增.删.改.查怎么连数据库:需要手动创 ...

  8. mysql的高阶用法_MySQL的经典用法(十四)-高级优化

    mysql的经典用法(十四)----高级优化 基于 /application/search/mysql/mysql-5.5.28/support-files/my-innodb-heavy-4G.cn ...

  9. Nginx高阶用法(一)

    Nginx 状态页   基于nginx模块ngx_http_auth_basic_module实现,在编译安装nginx的时候需要添加编译参数--with-http_stub_status_modul ...

  10. CASE WHEN 高阶用法?

    两个表做关联时,以左表为准,若左表某列不为空,则与右表对应列进行关联匹配,为空则不做匹配. 以上做法,有一种说不出来的感觉,不管怎样,问题是解决了. 如有更好的解决思路,请留言告知,不甚感激! 转载于 ...

最新文章

  1. 支持向量机SVM 参数选择
  2. python简笔画绘制 数据驱动绘图_pytorch visdom可视化工具学习—2—详细使用-2-plotting绘图...
  3. h2 mysql 兼容性_H2内存数据库对sql语句的支持问题 sql放到mysql数据库中能跑
  4. Python整数递增与++ [重复]
  5. thinkphp3.2.3 自定义路由实践
  6. 使用tracert命令查看某一个网站的ip地址
  7. lambda捕获this_非捕获Lambda的实例
  8. 教你玩转CSS 媒体类型
  9. Blend4精选案例图解教程(二):找张图片玩特效
  10. 17.2 无监督数据增强——UDA
  11. 2018/12/06 L1-022 L1-022 奇偶分家 Java
  12. 各种学习资料链接 干货 啃啃啃
  13. pth转onnx:RuntimeError: Exporting the operator uniform to ONNX opset version 9 is not supported.
  14. js QQ音乐歌词显示在浏览器标题
  15. Rme Babyface Pro FS娃娃脸声卡安装调试教程
  16. Python3.9.10标准库与语言参考等文档下载
  17. 三行代码可视化神经网络特征图
  18. 【笔记】unity渲染类名词术语概念总结(30个点)
  19. 教你简单4步制作“截图工具”,再也不用微信或QQ截图了
  20. Python知识体系图

热门文章

  1. c语言输出英文字母表,菜鸟求助,写一个随机输出26个英文字母的程序
  2. Python 学习总结(一):掌握基础知识,查缺补漏
  3. TableViewCell的折展(Masonry)
  4. 解决phpcms V9 推荐位无法排序
  5. 【转】关于23种设计模式的有趣见解
  6. ACdream 1728 SJY's First Task
  7. android 技术点记录
  8. 伪造邮件***,社工钓鱼,你中招了吗【一】
  9. static在实例Extends、Overload中理解
  10. 我的JDBC通用DAO(续)