写点什么

二分法求平方根,swift 面向协议编程 protocol 从入门到精通、《格局》吴军著读后感、John 易筋 ARTS 打卡 Week 27

用户头像
John(易筋)
关注
发布于: 2020 年 11 月 22 日

1. Algorithm: 每周至少做一个 LeetCode 的算法题

笔者的文章:

算法:二分法求平方根Sqrt(x)

题目

69. Sqrt(x)

Given a non-negative integer x, compute and return the square root of x.



Since the return type is an integer, the decimal digits are truncated, and only the integer part of the result is returned.



Example 1:



Input: x = 4
Output: 2



Example 2:



Input: x = 8
Output: 2



Explanation: The square root of 8 is 2.82842..., and since the decimal part is truncated, 2 is returned.

Constraints:

0 <= x <= 2^31 - 1



解题

二分法查找,平方小于等于x,则mid值有可能为最终结果,left = mid + 1;否则 right = mid - 1; 除法取整数,



def mySqrt1(self, x: int) -> int:
l, r, result = 0, x, x
while l <= r:
mid = l + (r - l) // 2
midPlus = mid * mid
if midPlus <= x:
l = mid + 1
result = mid
else:
r = mid - 1
return result



下面这种接法也可以

class Solution:
def mySqrt(self, x: int) -> int:
l, r = 0, x
while l <= r:
mid = l + (r - l) // 2
midPlus = mid * mid
if midPlus <= x < (mid + 1) * (mid + 1):
return mid
elif midPlus > x:
r = mid - 1
else:
l = mid + 1



Python中获取两数相除的商和余数

方法一:可以使用//求取两数相除的商、%求取两数相除的余数。[/在Python中获取的是相除的结果,一般为浮点数]

方法二:使用divmod()函数,获取商和余数组成的元祖



#!/usr/bin/python3
# -*- coding: utf-8 -*-
a = 10
b = 4
div = a // b
mod = a % b
print("{} / {} = {} ... {}".format(a, b, div, mod))
print("{} / {} = {} ... {}".format(a, b, *divmod(a, b)))
# print
# 10 / 4 = 2 ... 2
# 10 / 4 = 2 ... 2



参考

https://blog.csdn.net/dfq12345/article/details/78198797



2. Review: 阅读并点评至少一篇英文技术文章

笔者博客:

翻译:Swift 5.1中的Protocol面向协议的编程教程:从入门到精通

说明

在此面向协议protocol的编程教程中,您将学习有关扩展extensions,默认实现和其他将抽象添加到代码中的技术。



协议是Swift的基本功能。它们在Swift标准库的结构中起着主导作用,并且是一种常见的抽象方法。它们为某些其他语言提供的界面提供了类似的体验。



本教程将向您介绍称为面向协议的编程的软件工程实践,这已成为Swift的基础。如果您正在学习Swift,这确实是您需要掌握的东西!



在本教程中,您将了解:



  • 面向对象的编程和面向协议的编程之间的区别。

  • 具有默认实现的协议。

  • 扩展Swift标准库。

  • 使用泛型进一步扩展协议。



你在等什么?是时候启动您的Swift引擎了!



1. 入门

假设您正在开发赛车视频游戏。您希望玩家能够驾驶汽车,骑摩托车和驾驶飞机。他们甚至可以骑不同的鸟(因为这是电子游戏),您可以随心所欲地驾驶!这里的关键是可以驱动或操纵许多不同的“事物”。



此类应用程序的一种常见方法是面向对象的编程,您可以在其中封装所有逻辑,然后将其继承给其他类。基本类中将包含“驱动”和“飞行员”逻辑。



您可以通过为每种车辆创建类来开始对游戏进行编程。现在在鸟概念中添加图钉。您稍后将进行处理。



在编写代码时,您会注意到CarMotorcycle共享一些功能,因此创建了一个名为的基类MotorVehicle并将其添加到其中。Car并Motorcycle再继承MotorVehicle。您也设计称为基类Aircraft是Plane从继承。



您认为,“这很好。” 可是等等!您的赛车游戏设定为30XX年,有些汽车可以飞行。



现在,您面临困境。Swift不支持多重继承。您的飞行汽车如何从MotorVehicle和两者继承Aircraft?您是否创建了另一个合并了两个功能的基类?可能不是,因为没有干净简便的方法可以做到这一点。



谁能从这场灾难性的困境中拯救您的赛车游戏?面向协议Protocol的编程可以解救!

1.1 为什么要进行面向协议的编程?

协议允许您将相似的方法,功能和属性分组。斯威夫特,您可以指定这些接口的保证class,struct和enum类型。只有class类型可以使用基类和继承。



Swift中协议的优点是对象可以符合多种协议。



以这种方式编写应用程序时,您的代码将变得更加模块化。将协议视为功能的构建块。通过使对象符合协议来添加新功能时,不会构建一个全新的对象。那很费时间。而是,添加不同的构造块,直到对象准备就绪为止。



将基类转换为协议可以解决您的视频游戏难题。使用协议,您可以创建FlyingCar同时符合MotorVehicle和的类Aircraft。整洁吧?



是时候动手操作了,并尝试了这个赛车概念。



2. 实战

2.1 首先打开Xcode,然后创建一个名为SwiftProtocols.playground的新游乐场。



然后添加以下代码:

protocol Bird {
var name: String { get }
var canFly: Bool { get }
}
protocol Flyable {
var airspeedVelocity: Double { get }
}

使用Command-Shift-Return建立游乐场,以确保其正确编译。



这段代码定义了一个简单的协议Bird,带有属性name和canFly。然后定义一个名为的协议Flyable,该协议具有属性airspeedVelocity。



在过去的协议时代,开发人员将以Flyable基类作为开始,然后依靠对象继承来定义Bird和运行其他所有东西。



但是在面向协议的编程中,一切都从协议开始。此技术使您可以封装功能概念,而无需基类。



如您所见,这在定义类型时使整个系统更加灵活。

2.2 定义符合协议的类型

首先struct在运动场的底部添加以下定义:



struct FlappyBird: Bird, Flyable {
let name: String
let flappyAmplitude: Double
let flappyFrequency: Double
let canFly = true
var airspeedVelocity: Double {
3 * flappyFrequency * flappyAmplitude
}
}

这段代码定义了一个新的struct名称FlappyBird,该名称同时符合Bird和Flyable协议。它airspeedVelocity是包含flappyFrequency和的计算属性flappyAmplitude。作为飞扬的,它返回true的canFly。



2.3 接下来,struct在运动场的底部添加以下两个定义:



struct Penguin: Bird {
let name: String
let canFly = false
}
struct SwiftBird: Bird, Flyable {
var name: String { "Swift \(version)" }
let canFly = true
let version: Double
private var speedFactor = 1000.0
init(version: Double) {
self.version = version
}
// Swift is FASTER with each version!
var airspeedVelocity: Double {
version * speedFactor
}
}

Penguin是Bird,但不能飞行。好东西,您没有采用继承方法,而是制作了所有鸟Flyable!



使用协议,您可以定义功能组件并使任何相关对象符合它们。



然后您进行声明SwiftBird,但是在我们的游戏中有的不同版本SwiftBird。该version属性越高,其airspeedVelocity由计算属性定义的速度越快。



但是,您会看到有冗余。每种类型的项Bird都必须声明它是否存在,canFly即使Flyable您的系统中已经存在一个概念。几乎就像您需要一种定义协议方法的默认实现的方法一样。嗯,这就是协议扩展的用武之地。



2.4 使用默认实现扩展协议

协议扩展允许您定义协议的默认行为。要实现第一个,请在Bird协议定义下方插入以下内容:



extension Bird {
// Flyable birds can fly!
var canFly: Bool { self is Flyable }
}

此代码在上定义了扩展名Bird。它将默认行为设置为在类型符合协议时canFly返回。换句话说,任何鸟都不再需要显式声明它。它会像大多数鸟类一样飞翔。trueFlyableFlyablecanFly



现在删除let canFly = ...的FlappyBird,Penguin和SwiftBird。再次建造游乐场。您会注意到,游乐场仍然可以成功构建,因为协议扩展现在可以满足该要求。



2.5 枚举也可以玩

EnumSwift中的类型比C和C ++中的枚举功能强大得多。它们采用了仅传统上class或struct类型支持的许多功能,这意味着它们可以符合协议。



enum在游乐场的末尾添加以下定义:



enum UnladenSwallow: Bird, Flyable {
case african
case european
case unknown
var name: String {
switch self {
case .african:
return "African"
case .european:
return "European"
case .unknown:
return "What do you mean? African or European?"
}
}
var airspeedVelocity: Double {
switch self {
case .african:
return 10.0
case .european:
return 9.9
case .unknown:
fatalError("You are thrown from the bridge of death!")
}
}
}

通过定义正确的属性,UnladenSwallow符合两国的协议Bird和Flyable。因为它是这样的遵循者,所以它也享有的默认实现canFly。



您是否真的认为涉及的教程airspeedVelocity可以忽略Monty Python参考资料?:]



2.6 覆盖默认行为

您的UnladenSwallow类型canFly通过遵守Bird协议自动收到了的实现。但是,你要UnladenSwallow.unknown回false了canFly。



您可以覆盖默认实现吗?你打赌 回到操场的尽头,添加一些新代码:



extension UnladenSwallow {
var canFly: Bool {
self != .unknown
}
}

现在只有.african和.european返回true的canFly。试试看!在操场的尽头添加以下代码:



UnladenSwallow.unknown.canFly // false
UnladenSwallow.african.canFly // true
Penguin(name: "King Penguin").canFly // false

建造游乐场,您会注意到它显示了上面注释中给出的值。



这样,您就可以像在面向对象编程中使用虚拟方法那样覆盖属性和方法。



2.7 扩展协议

您还可以使自己的协议与Swift标准库中的其他协议保持一致,并定义默认行为。将Bird协议声明替换为以下代码:



protocol Bird: CustomStringConvertible {
var name: String { get }
var canFly: Bool { get }
}
extension CustomStringConvertible where Self: Bird {
var description: String {
canFly ? "I can fly" : "Guess I'll just sit here :["
}
}

符合CustomStringConvertible意味着您的类型需要具有description属性,以便String在需要时将其自动转换为属性。Bird您并未定义CustomStringConvertible将仅与类型关联的协议扩展,而不是将此属性添加到当前和将来的每种类型Bird。



在操场底部输入以下内容进行尝试:



UnladenSwallow.african

建立游乐场,您应该I can fly会在助手编辑器中看到“ ”出现。恭喜你!您已经扩展了协议。



2.8 对Swift标准库的影响

协议扩展不能像皮一样抓一磅重的椰子,但是如您所见,它们可以提供一种自定义和扩展命名类型功能的有效方法。Swift团队还采用协议来改进Swift标准库。



将此代码添加到游乐场的末尾:



let numbers = [10, 20, 30, 40, 50, 60]
let slice = numbers[1...3]
let reversedSlice = slice.reversed()
let answer = reversedSlice.map { $0 * 10 }
print(answer)

您也许可以猜出答案,但是可能令人惊讶的是所涉及的类型。



例如,slice不是Array<Int>而是ArraySlice<Int>。这种特殊的包装器类型充当原始数组的视图,提供了一种快速有效的方法来对较大数组的各个部分执行操作。同样,reversedSlice是的ReversedCollection<ArraySlice<Int>>另一个包装器类型,具有原始数组的视图。



幸运的是,开发Swift标准库的向导将map功能定义为Sequence协议的扩展,所有Collection类型都遵循该协议。这样一来,您map就Array可以像在上一样轻松地进行调用,ReversedCollection而不会注意到它们之间的差异。您很快就会借用这一重要的设计模式。



2.9 去比赛

到目前为止,您已经定义了几种符合的类型Bird。现在,您将在操场的尽头添加完全不同的内容:



class Motorcycle {
init(name: String) {
self.name = name
speed = 200.0
}
var name: String
var speed: Double
}

这个班级与鸟类或飞行无关。您只想让摩托车与企鹅竞赛。是时候将这些古怪的赛车手带到起跑线上了。

2.10 汇集全部

为了统一这些不同的类型,您需要一个通用的赛车协议。得益于一种称为追溯建模的好主意,您甚至可以在不触及原始模型定义的情况下进行管理。只需将以下内容添加到您的游乐场:



// 1
protocol Racer {
var speed: Double { get } // speed is the only thing racers care about
}
// 2
extension FlappyBird: Racer {
var speed: Double {
airspeedVelocity
}
}
extension SwiftBird: Racer {
var speed: Double {
airspeedVelocity
}
}
extension Penguin: Racer {
var speed: Double {
42 // full waddle speed
}
}
extension UnladenSwallow: Racer {
var speed: Double {
canFly ? airspeedVelocity : 0.0
}
}
extension Motorcycle: Racer {}
// 3
let racers: [Racer] =
[UnladenSwallow.african,
UnladenSwallow.european,
UnladenSwallow.unknown,
Penguin(name: "King Penguin"),
SwiftBird(version: 5.1),
FlappyBird(name: "Felipe", flappyAmplitude: 3.0, flappyFrequency: 20.0),
Motorcycle(name: "Giacomo")]

这是这样做的:



  1. 首先,定义协议Racer。该协议定义了您的游戏中可以竞争的所有内容。

  2. 然后,Racer使所有内容都符合,以便我们所有现有的类型都可以竞争。某些类型(例如)Motorcycle琐碎地符合。其他的(例如UnladenSwallow)则需要更多的逻辑。无论哪种方式,当您完成操作时,都会得到一堆符合标准的Racer类型。

  3. 在起始处使用所有类型时,现在创建一个Array<Racer>,其中包含您所创建的每种类型的实例。



建立操场检查所有编译。

2.11 最高速度

是时候编写一个确定赛车手最高速度的函数了。将以下代码添加到游乐场的末尾:



func topSpeed(of racers: [Racer]) -> Double {
racers.max(by: { $0.speed < $1.speed })?.speed ?? 0.0
}
topSpeed(of: racers) // 5100

此函数使用Swift标准库函数max查找速度最高的赛车并返回。如果用户在中传入空值Array,则返回0.0 racers。



建立操场,您会看到您先前创建的赛车的最大速度确实是5100。



2.12 使它更通用

假设Racers规模很大,您只想找到一部分参与者的最高速度。解决的办法是改变topSpeed(of:)以采取任何Sequence不是具体的东西Array。



使用topSpeed(of:)以下功能替换您现有的实现:



// 1
func topSpeed<RacersType: Sequence>(of racers: RacersType) -> Double
/*2*/ where RacersType.Iterator.Element == Racer {
// 3
racers.max(by: { $0.speed < $1.speed })?.speed ?? 0.0
}

这可能看起来有点吓人,但它是如何分解的:



  1. RacersType是此函数的通用类型。它可以是符合Swift标准库Sequence协议的任何类型。

  2. 该where子句指定的Element类型Sequence必须符合您的Racer协议才能使用此功能。

  3. 实际的功能主体与以前相同。



现在,将以下代码添加到操场的底部:



topSpeed(of: racers[1...3]) // 42

建立游乐场,您将看到输出为42的答案。该函数现在适用于任何Sequence类型,包括ArraySlice。



2.13 使它更迅速

这是一个秘密:您可以做得更好。在操场的尽头添加以下内容:



extension Sequence where Iterator.Element == Racer {
func topSpeed() -> Double {
self.max(by: { $0.speed < $1.speed })?.speed ?? 0.0
}
}
racers.topSpeed() // 5100
racers[1...3].topSpeed() // 42

从Swift标准库剧本中借用,您现在已经扩展了Sequence自身以具有topSpeed()功能。该功能是很容易发现的,只有当你是在处理一个应用Sequence的Racer类型。



2.14 协议比较器

Swift协议的另一个功能是如何表示操作员要求,例如的对象相等==或如何比较>和的对象<。您知道这笔交易吧–在您的游乐场底部添加以下代码:



protocol Score {
var value: Int { get }
}
struct RacingScore: Score {
let value: Int
}

拥有Score协议意味着您可以编写以相同方式对待所有分数的代码。但是,通过使用不同的具体类型(例如)RacingScore,您不会将这些分数与样式分数或可爱分数混为一谈。谢谢,编译器!



您希望分数具有可比性,以便您可以判断谁得分最高。在Swift 3之前,开发人员需要添加全局运算符功能以符合这些协议。今天,您可以将这些静态方法定义为模型的一部分。通过替换的定义,这样做Score,并RacingScore有以下情况:



protocol Score: Comparable {
var value: Int { get }
}
struct RacingScore: Score {
let value: Int
static func <(lhs: RacingScore, rhs: RacingScore) -> Bool {
lhs.value < rhs.value
}
}

真好!您已经将所有逻辑封装RacingScore在一个地方。Comparable只需要您为小于运算符提供一个实现。其余要比较的运算符(例如大于),具有Swift标准库基于小于运算符提供的默认实现。



在游乐场底部使用以下代码行测试新发现的操作员技能:



RacingScore(value: 150) >= RacingScore(value: 130) // true

建立游乐场,您会注意到答案是true预期的。您现在可以比较分数!

2.15 变异功能

到目前为止,您实现的每个示例都演示了如何添加功能。但是,如果您想让协议定义一些可以改变对象外观的东西,该怎么办?您可以通过在协议中使用变异方法来做到这一点。



在操场的底部,添加以下新协议:



protocol Cheat {
mutating func boost(_ power: Double)
}

这定义了一种协议,可使您的类型作弊。怎么样?通过增加您认为合适的任何东西。



接下来,在扩展上创建SwiftBird符合Cheat以下代码的扩展:



extension SwiftBird: Cheat {
mutating func boost(_ power: Double) {
speedFactor += power
}
}

在这里,您实现boost(_:)并speedFactor通过power传入来增加值。您添加了mutating关键字以struct使它的值之一将在此函数中更改。



将以下代码添加到操场上,以了解其工作原理:



var swiftBird = SwiftBird(version: 5.0)
swiftBird.boost(3.0)
swiftBird.airspeedVelocity // 5015
swiftBird.boost(3.0)
swiftBird.airspeedVelocity // 5030

在这里,您创建了一个SwiftBird可变的a,并将其速度提高了3,然后又提高了3。建造游乐场,您应注意,每次提升时airspeedVelocity的SwiftBird都增加了。



3. 然后去哪儿?

使用本教程顶部或底部的下载材料按钮下载完整的Playground。



至此,您已经通过创建简单协议并使用协议扩展对其进行了扩展,从而测试了面向协议编程的功能。使用默认实现,您可以为现有协议提供常见和自动的行为。这就像更好地像基类一样,因为它们也可以应用于struct和enum类型。



您还已经看到协议扩展可以扩展并为Swift标准库,Cocoa,Cocoa Touch或任何第三方库中的协议提供默认行为。



要继续学习有关协议的更多信息,请阅读Swift的官方文档



您可以在Apple的开发人员门户上观看有关面向协议编程的WWDC精彩会议。它提供了对所有背后理论的深入探索。



在其Swift进化提案中阅读有关操作符一致性的基本原理。您可能还想了解有关SwiftCollection协议的更多信息,并[学习如何构建自己的协议](https://www.raywenderlich.com/139591/building-custom-collection-swift)



与任何编程范例一样,很容易变得过于旺盛并将其用于所有事物。克里斯·艾德霍夫(Chris Eidhof)的这篇有趣的博客文章提醒读者,他们应该提防银弹解决方案。不要在各处仅因为“使用协议”。



参考

https://www.raywenderlich.com/6742901-protocol-oriented-programming-tutorial-in-swift-5-1-getting-started



3. Tips: 学习至少一个技术技巧

笔者的文章:

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

说明

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) {
// 2
precondition(occurrences > 0,
"Can only add a positive number of occurrences")
// 3
if 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) {
// 1
guard
let currentCount = contents[member],
currentCount >= occurrences
else {
return
}
// 2
precondition(occurrences > 0,
"Can only remove a positive number of occurrences")
// 3
if 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) where
S.Iterator.Element == Element {
for element in sequence {
add(element)
}
}
// 3
init<S: Sequence>(_ sequence: S) where
S.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 {
// 1
typealias Iterator = DictionaryIterator<Element, Int>
// 2
func makeIterator() -> Iterator {
// 3
return 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 {
// 1
typealias Iterator = AnyIterator<(element: Element, count: Int)>
func makeIterator() -> Iterator {
// 2
var iterator = contents.makeIterator()
// 3
return 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 {
// 1
typealias Index = DictionaryIndex<Element, Int>
// 2
var startIndex: Index {
return contents.startIndex
}
var endIndex: Index {
return contents.endIndex
}
// 3
subscript (position: Index) -> Iterator.Element {
precondition(indices.contains(position), "out of bounds")
let dictionaryElement = contents[position]
return (element: dictionaryElement.key,
count: dictionaryElement.value)
}
// 4
func 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> {
// 2
fileprivate let index: DictionaryIndex<Element, Int>
// 3
fileprivate 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 {
// 1
typealias Index = BagIndex<Element>
var startIndex: Index {
// 2.1
return BagIndex(contents.startIndex)
}
var endIndex: Index {
// 2.2
return BagIndex(contents.endIndex)
}
subscript (position: Index) -> Iterator.Element {
precondition((startIndex ..< endIndex).contains(position),
"out of bounds")
// 3
let dictionaryElement = contents[position.index]
return (element: dictionaryElement.key,
count: dictionaryElement.value)
}
func index(after i: Index) -> Index {
// 4
return 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] }) {
// 4
let basketElement = fruitBasket[fruitMinIndex]
let sliceElement = fruitSlice[fruitMinIndex]
precondition(basketElement == sliceElement,
"Expected basketElement and sliceElement to be the same element")
}

再次运行游乐场。



在上面的代码中,您:



  1. 创建一个由四个不同水果组成的水果篮。

  2. 取出第一种水果。实际上,这只是在水果篮中创建一个新的切片视图(不包括您删除的第一个元素),而不是创建一个全新的Bag对象。您会在结果栏中注意到这里的类型是Slice<Bag<String>>。

  3. 在剩余的水果中找到最少出现的水果的索引。

  4. 证明即使从切片中计算索引,您也可以使用基础集合和切片中的索引来检索相同的元素。



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



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

4 然后去哪儿?

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



在本教程中,您学习了如何在Swift中创建自定义集合。您为Sequence,[Collection](https://developer.apple.com/documentation/swift/collection),[CustomStringConvertible](https://developer.apple.com/documentation/swift/customstringconvertible/),[ExpressibleByArrayLiteral](https://developer.apple.com/documentation/swift/expressiblebyarrayliteral),[ExpressibleByDictionaryLiteral](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral)添加了一致性,并创建了自己的索引类型。



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



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





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



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



参考

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



4. Share: 分享一篇有观点和思考的技术文章

《格局》 吴军著 读后感-- 跟每个人都相关的李嘉图定律 - 免费的不值钱

大卫·李嘉图(David Ricardo, 1772.04.18—1823.09.11)

古典经济学理论的完成者,古典学派的最后一名代表,最有影响力的古典经济学家



  英国古典政治经济学的代表。生于犹太人家庭,父亲为证券交易所经纪人。12岁到荷兰商业学校学习,14岁随父从事证券交易。1793年独立开展证券交易活动,25岁时拥有200万英镑财产,随后钻研数学、物理学。1799年读 亚当·斯密<《国富论》后开始研究经济问题,参加了当时关于 黄金价格 和谷物法的讨论,1817年发表《政治经济学及赋税原理》,1819年选为下议院议员。



李嘉图定律 -- 租金定律

今天说的李嘉图定律,又被成为租金定律:



土地租金是指土地使用者所支付的价格,它是由垄断性(稀缺性)来决定的,而不是取决于地主在上面做的投资和改良的成本。它的价格受限于租用者(农民)能够承担的价格。



那么最高的租金和落差是如何确定的呢?李嘉图的租金定律给出了两种决定因素,即不同土地收入的差额,以及和其他投资资本回报的对比。



李嘉图定律很快被延伸到其他带有稀缺性质的经济要素中,而且有被无限延伸的趋势。比如古典经济学大师约翰·穆勒把李嘉图定律延伸到知识产权,因为它也有独占性。通过知识产权获得的利润也被穆勒理解为租金。



到了信息时代,李嘉图定律被赋予了新的含义,即对能够比较出的优势进行定价。在IT行业里,工程师之间的三六九等,不同产品和服务之间的三六九等就被划分出来了。



今天,李嘉图定律被赋予的第二个新的含义是,信息越透明,越对称,流动性越好,李嘉图定律导致的势差就会越大。这件事其实不是现在才有的,实际上当大众商品、大众传媒出现之后,苗头就出现了。



第一张是李嘉图那个时代地租的势差,从中心到周边再到海边盐碱地,从高到零,是渐变的。



第二张图是当今带有稀缺性质生产要素和商品势差的变化,是陡降的。我在几周前的问答中谈到了硅谷的房价。很多人觉得北京学区房的房价比周边一公里处高50%就是了不得的事情了,其实在硅谷,隔着一条高速,房价差三倍也不稀奇(Palo Alto 和East Palo Alto),这就是李嘉图定律今天的含义。随着互联网将一切变得透明,李嘉图定律带来的势差是不断地被放大。它体现在很多方面,比如:



  1. 一流工程师的收入是天价,末流的免费给人工作。一些美国的游戏开发者和我讲,他们的行业中,一些工程师的收入一个月不到1000美元,比打扫厕所的人都低很多。我说这很正常,这不是社会公平不公平,而是李嘉图定律决定的。事实上,中国最好的游戏设计者一年能挣大约一个亿。



  1. 绝大部分IT服务,比如各种App不仅不能挣钱,而且是倒贴钱请人使用的。但是,好的App几年能挣上亿美元。



  1. 无论是电商、移动支付还是O2O的服务,第二名永远无法拿到第一名的估值,第三名之后价值几乎等于零。



  1. 在半导体行业任何一个细分市场,第一名拿走了几乎全部利润,第二名勉强能盈利,第三名之后都在亏损。



  1. 任何专业人士(律师、会计师、投资经理等等),一等水平的人的收入是行业平均水平的几倍到十几倍。



概括来讲,信息越透明,这种趋势越明显,而不像很多人想的那样,互联网会带来大家收入的趋同,恰恰相反,互联网的发展会带来李嘉图定律的放大效果。我还可以预言,将来好学校、好医院、好地段的房子,会越来越紧缺,而不是稀缺性问题得到缓解。



作为一个凡人,我始终奉行“世界上真正的老大是上帝”的原则。像摩尔定律、李嘉图定律这些规则,是上帝设定的,任何人都拗不过它们。你信也好,不信也罢,它们永远在左右着世界。比如一定要限制黄金地段新房的房价,结果就是杭州、深圳的市民不上班,天天排队拿号买房子。作为凡人,能做的就是按照这些定律所说的办。比如买房子,千万不要贪便宜找一个烂地段去买,再漂亮的新房子都会贬值,只有好地段的土地才会升值。办公司,千万不要为了得到一点点政策优惠,跑到三线城市去。公司雇人,千万不要贪便宜雇一大堆三流人士充数。



同样,作为人:如果不能做到比周围人明显高出一截,那么劳动都是免费的、不值钱的。这就如同荒滩盐碱地,没有人愿意付钱租用一样。这也就是我为什么老说,十个九十分,也抵不上一个100分的原因。



当然有人可能会觉得我说得太狠,但是即使我很有同情心也没有用,因为上帝在使用摩尔定律、李嘉图定律悄无声息地给大自然修剪枝叶呢!处在信息时代,就要了解它的定律,并且把它们写入我们的行动指南。



感想

靠口才狡辩让人无话可说其实并没有说服人,事实比口才更重要。这也就是我推荐的以时间为朋友,10年如一日的看这个人做了什么,而不是说了什么。一个人明年比今年进步,后年比明年进步,对自身和身边人的输出就是正向的,幸福感就会是递增的。



这个世界是分层的,假如分了100层。我认为我是处于最底层的100层。如果生活与老家,在小地方,大家都处于最底层,因为差不多,所以没什么感觉。如果你在北上广深四大城市,这里从第一层到第100层的人都有,那么就会有特别大的感触了。在最底层的人,每天用了100%的努力,想往上走个几层,可能比不过处于70层每天只用了10%的努力。这就是现实。



即使在老毛时代,社会也是分层的,最底层人想进城都比较困难,进城以后,拉黄包车的、骑自行车的、开小车的都是很分明的,更不要说平民百姓跟官员的区别。



小地方新婚夫妻,10年目标做到两人互相了解、互相扶持、互相照顾。由内而外的感染身边的人感受爱的力量。而不是靠嘴说,靠流言蜚语吹嘘。(小地方的人无所事事比较多,流言都没有营养。)幸福的家庭都是相似的,不幸的家庭各有各的不幸。多向幸福的家庭学习。



20年目标,在于由内而外地影响身边人,让家庭中的人受到你们的感染成为一家人,而不是说教。如果自己小两口都没处理好,就处理好两人的小生活先把。



爱自己原则

一切利他的思想、语言和行为的开端,就是接受自己的一切并真心喜爱自己。只有这样,你才能爱别人,才能爱世界,你才可能有真正的欢喜、安定和无畏,才可能有广阔的胸襟。

你如果不喜欢不满意自己,那么你是无法真正喜欢别人的。这点非常重要。有些人把爱自己等同于自私自利,这是误解。

  如果仔细体会,就会发现,你如果对自己不喜欢、不满意,就很容易生出嫉妒心和怨恨心。

自己也是众生中的一员,爱众生的同时为何把自己排除在外?所以请先好好认识自己,先跟自己做好朋友,再谈爱其他众生。

#心理学#



特别赞的总结 概念 ·定律 ·效应 ·法则



“我们处于信息时代,所以我们必须了解这个时代的定律,并且把这些定律作为我们的行动指南。”——吴军 “除非有更好的选择,否则就遵从标准。但从实际出发,这些法则定了起到的只是参考或启发作为。”——交互设计之父阿兰·库珀



参考

https://www.yuque.com/alisunstar/cp5mav



https://kuaibao.qq.com/s/20191108AZOYKX00?refer=spider



https://wiki.mbalib.com/wiki/%E5%A4%A7%E5%8D%AB%C2%B7%E6%9D%8E%E5%98%89%E5%9B%BE



https://wiki.mbalib.com/wiki/%E6%9D%8E%E5%98%89%E5%9B%BE%E7%9A%84%E6%AF%94%E8%BE%83%E4%BC%98%E5%8A%BF%E7%90%86%E8%AE%BA



发布于: 2020 年 11 月 22 日阅读数: 59
用户头像

John(易筋)

关注

问渠那得清如许?为有源头活水来 2018.07.17 加入

工作10+年,架构师,曾经阿里巴巴资深无线开发,汇丰银行架构师/专家。开发过日活过亿的淘宝Taobao App,擅长架构、算法、数据结构、设计模式、iOS、Java Spring Boot。易筋为阿里巴巴花名。

评论

发布
暂无评论
二分法求平方根,swift面向协议编程protocol从入门到精通、《格局》吴军著读后感、John 易筋 ARTS 打卡 Week 27