Swift基礎(chǔ)知識(shí)相關(guān)(三) —— 重載自定義運(yùn)算符(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2019.08.13 星期二

前言

這個(gè)專(zhuān)題我們就一起看一下Swfit相關(guān)的基礎(chǔ)知識(shí)拯刁。感興趣的可以看上面幾篇菩混。
1. Swift基礎(chǔ)知識(shí)相關(guān)(一) —— 泛型(一)
2. Swift基礎(chǔ)知識(shí)相關(guān)(二) —— 編碼和解碼(一)

開(kāi)始

首先看下主要內(nèi)容

主要內(nèi)容:在本Swift教程中,您將學(xué)習(xí)如何創(chuàng)建自定義運(yùn)算符扁藕,重載現(xiàn)有運(yùn)算符以及設(shè)置運(yùn)算符優(yōu)先級(jí)沮峡。

接著,看下寫(xiě)作環(huán)境

Swift 5, iOS 13, Xcode 11

運(yùn)算符是任何編程語(yǔ)言的核心構(gòu)建塊亿柑。你能想象編程而不使用+=嗎邢疙?

運(yùn)算符非常基礎(chǔ)望薄,大多數(shù)語(yǔ)言都將它們作為編譯器(或解釋器)的一部分疟游。另一方面,Swift編譯器并不對(duì)大多數(shù)操作符進(jìn)行硬編碼式矫,而是為庫(kù)提供了創(chuàng)建自己的操作符的方法乡摹。它將工作留給了Swift標(biāo)準(zhǔn)庫(kù)(Swift Standard Library),以提供您期望的所有常見(jiàn)標(biāo)準(zhǔn)庫(kù)采转。這種差異是微妙的聪廉,但為巨大的定制潛力打開(kāi)了大門(mén)。

Swift運(yùn)算符特別強(qiáng)大故慈,因?yàn)槟梢酝ㄟ^(guò)兩種方式更改它們以滿足您的需求:為現(xiàn)有運(yùn)算符分配新功能(稱(chēng)為運(yùn)算符重載 operator overloading)板熊,以及創(chuàng)建新的自定義運(yùn)算符。

在本教程中察绷,您將使用一個(gè)簡(jiǎn)單的Vector結(jié)構(gòu)體并構(gòu)建自己的一組運(yùn)算符干签,以幫助組合不同的向量。

打開(kāi)Xcode拆撼,然后轉(zhuǎn)到File?New?Playground創(chuàng)建一個(gè)新playground容劳。選擇Blank模板并命名您的playgroundCustomOperators喘沿。刪除所有默認(rèn)代碼,以便您可以從空白平板開(kāi)始竭贩。

將以下代碼添加到您的playground

struct Vector {
  let x: Int
  let y: Int
  let z: Int
}

extension Vector: ExpressibleByArrayLiteral {
  init(arrayLiteral: Int...) {
    assert(arrayLiteral.count == 3, "Must initialize vector with 3 values.")
    self.x = arrayLiteral[0]
    self.y = arrayLiteral[1]
    self.z = arrayLiteral[2]
  }
}

extension Vector: CustomStringConvertible {
  var description: String {
    return "(\(x), \(y), \(z))"
  }
}

在這里蚜印,您可以定義一個(gè)新的Vector類(lèi)型,其中三個(gè)屬性符合兩個(gè)協(xié)議留量。 CustomStringConvertible協(xié)議和description計(jì)算屬性允許您打印Vector的友好字符串表示窄赋。

playground的底部,添加以下行:

let vectorA: Vector = [1, 3, 2]
let vectorB = [-2, 5, 1] as Vector

你剛剛用簡(jiǎn)單的數(shù)組創(chuàng)建了兩個(gè)向量Vectors楼熄,沒(méi)有初始化器忆绰!那是怎么發(fā)生的?

ExpressibleByArrayLiteral協(xié)議提供無(wú)摩擦的接口來(lái)初始化Vector可岂。該協(xié)議需要一個(gè)具有可變參數(shù)的不可用初始化程序:init(arrayLiteral:Int ...)错敢。

可變參數(shù)arrayLiteral允許您傳入由逗號(hào)分隔的無(wú)限數(shù)量的值。例如青柄,您可以創(chuàng)建Vector伐债,例如Vector(arrayLiteral:0)Vector(arrayLiteral:5,4,3)

該協(xié)議進(jìn)一步方便致开,并允許您直接使用數(shù)組進(jìn)行初始化峰锁,只要您明確定義類(lèi)型,這是您為vectorAvectorB所做的双戳。

這種方法的唯一警告是你必須接受任何長(zhǎng)度的數(shù)組虹蒋。如果您將此代碼放入應(yīng)用程序中,請(qǐng)記住飒货,如果傳入長(zhǎng)度不是三的數(shù)組魄衅,它將會(huì)崩潰。如果您嘗試初始化少于或多于三個(gè)值的Vector塘辅,則初始化程序頂部的斷言assert將在開(kāi)發(fā)和內(nèi)部測(cè)試期間在控制臺(tái)中提醒您晃虫。

單獨(dú)的矢量Vectors很好,但如果你能用它們做事情會(huì)更好扣墩。正如你在小學(xué)時(shí)所做的那樣哲银,你將從加法開(kāi)始你的學(xué)習(xí)之旅。


Overloading the Addition Operator

運(yùn)算符重載的一個(gè)簡(jiǎn)單示例是加法運(yùn)算符呻惕。 如果您將它與兩個(gè)數(shù)字一起使用荆责,則會(huì)發(fā)生以下情況:

1 + 1 // 2

但是,如果對(duì)字符串使用相同的加法運(yùn)算符亚脆,則它具有完全不同的行為:

"1" + "1" // "11"

當(dāng)+與兩個(gè)整數(shù)一起使用時(shí)做院,它會(huì)以算術(shù)形式添加它們。 但是當(dāng)它與兩個(gè)字符串一起使用時(shí),它會(huì)將它們連接起來(lái)键耕。

為了使運(yùn)算符重載寺滚,您必須實(shí)現(xiàn)一個(gè)名稱(chēng)為運(yùn)算符符號(hào)的函數(shù)。

注意:您可以將重載函數(shù)定義為類(lèi)型的成員屈雄,這是您將在本教程中執(zhí)行的操作玛迄。 這樣做時(shí),必須將其聲明為靜態(tài)static棚亩,以便可以在沒(méi)有定義它的類(lèi)型的實(shí)例的情況下訪問(wèn)它。

playground的尾部添加以下代碼:

// MARK: - Operators
extension Vector {
  static func + (left: Vector, right: Vector) -> Vector {
    return [
      left.x + right.x,
      left.y + right.y,
      left.z + right.z
    ]
  }
}

此函數(shù)將兩個(gè)向量作為參數(shù)虏杰,并將它們的和作為新向量返回讥蟆。 要做矢量加法,只需相加其各個(gè)組件即可纺阔。

要測(cè)試此功能瘸彤,請(qǐng)將以下內(nèi)容添加到playground的底部:

vectorA + vectorB // (-1, 8, 3)

您可以在playground的右側(cè)邊欄中看到合成矢量。

1. Other Types of Operators

加法運(yùn)算符是所謂的中綴infix運(yùn)算符笛钝,意味著它在兩個(gè)不同的值之間使用质况。 還有其他類(lèi)型的運(yùn)算符:

  • infix:在兩個(gè)值之間使用,例如加法運(yùn)算符(例如玻靡,1 + 1
  • prefix:在值之前添加结榄,如負(fù)號(hào)運(yùn)算符(例如 -3)。
  • postfix:在一個(gè)值之后添加囤捻,比如force-unwrap運(yùn)算符(例如臼朗,mayBeNil!
  • ternary:在三個(gè)值之間插入兩個(gè)符號(hào)蝎土。 在Swift中视哑,不支持用戶定義的三元運(yùn)算符,只有一個(gè)內(nèi)置的三元運(yùn)算符誊涯,您可以在 Apple’s documentation中閱讀挡毅。

您想要重載的下一個(gè)運(yùn)算符是負(fù)號(hào)符號(hào),它將更改Vector的每個(gè)組件的符號(hào)暴构。 例如跪呈,如果將它應(yīng)用于vectorA,即(1,3,2)丹壕,則返回(-1庆械,-3,-2)菌赖。

在擴(kuò)展名內(nèi)的上一個(gè)靜態(tài)static函數(shù)下面添加此代碼:

static prefix func - (vector: Vector) -> Vector {
  return [-vector.x, -vector.y, -vector.z]
}

假設(shè)運(yùn)算符是中綴infix缭乘,因此如果您希望運(yùn)算符是不同的類(lèi)型,則需要在函數(shù)聲明中指定運(yùn)算符類(lèi)型。 負(fù)號(hào)運(yùn)算符不是中綴堕绩,因此您將前綴prefix修飾符添加到函數(shù)聲明中策幼。

playground的底部,添加以下行:

-vectorA // (-1, -3, -2)

在側(cè)欄中檢查結(jié)果是否正確奴紧。

接下來(lái)是減法特姐,留給你自己實(shí)現(xiàn)。 完成后黍氮,請(qǐng)檢查以確保您的代碼與我的代碼類(lèi)似唐含。 提示:減法與添加負(fù)號(hào)相同。

試一試沫浆,如果您需要幫助捷枯,請(qǐng)查看下面的解決方案!

static func - (left: Vector, right: Vector) -> Vector {
  return left + -right
}

通過(guò)將此代碼添加到playground的底部來(lái)測(cè)試您的新運(yùn)算符:

vectorA - vectorB // (3, -2, 1)

2. Mixed Parameters? No Problem!

您還可以通過(guò)標(biāo)量乘法將向量乘以數(shù)字专执。 要將兩個(gè)向量相乘淮捆,可以將每個(gè)分量相乘。 你接下來(lái)要實(shí)現(xiàn)這個(gè)本股。

您需要考慮的一件事是參數(shù)的順序攀痊。 當(dāng)您實(shí)施加法時(shí),順序無(wú)關(guān)緊要拄显,因?yàn)閮蓚€(gè)參數(shù)都是向量苟径。

對(duì)于標(biāo)量乘法,您需要考慮Int * VectorVector * Int凿叠。 如果您只實(shí)現(xiàn)其中一種情況涩笤,Swift編譯器將不會(huì)自動(dòng)知道您希望它以其他順序工作。

要實(shí)現(xiàn)標(biāo)量乘法盒件,請(qǐng)?jiān)趧倓偺砑拥臏p法函數(shù)下添加以下兩個(gè)函數(shù):

static func * (left: Int, right: Vector) -> Vector {
  return [
    right.x * left,
    right.y * left,
    right.z * left
  ]
}

static func * (left: Vector, right: Int) -> Vector {
  return right * left
}

為避免多次寫(xiě)入相同的代碼蹬碧,第二個(gè)函數(shù)只是將其參數(shù)轉(zhuǎn)發(fā)給第一個(gè)。

在數(shù)學(xué)中炒刁,向量有另一個(gè)有趣的操作恩沽,稱(chēng)為cross-productcross-product的原理超出了本教程的范圍翔始,但您可以在Cross product Wikipedia page頁(yè)面上了解有關(guān)它們的更多信息罗心。

由于在大多數(shù)情況下不鼓勵(lì)使用自定義符號(hào)(誰(shuí)想在編碼時(shí)打開(kāi)表情符號(hào)菜單?)城瞎,重復(fù)使用星號(hào)和cross-product運(yùn)算符會(huì)非常方便渤闷。

與標(biāo)量乘法不同,Cross-products將兩個(gè)向量作為參數(shù)并返回一個(gè)新向量脖镀。

添加以下代碼以在剛剛添加的乘法函數(shù)之后添加cross-product實(shí)現(xiàn):

static func * (left: Vector, right: Vector) -> Vector {
  return [
    left.y * right.z - left.z * right.y,
    left.z * right.x - left.x * right.z,
    left.x * right.y - left.y * right.x
  ]
}

現(xiàn)在飒箭,將以下計(jì)算添加到playground的底部,同時(shí)利用乘法和cross-product運(yùn)算符:

vectorA * 2 * vectorB // (-14, -10, 22)

此代碼找到vectorA2的標(biāo)量倍數(shù),然后找到該向量與vectorB的交叉乘積弦蹂。 請(qǐng)注意肩碟,星號(hào)運(yùn)算符始終從左向右,因此前面的代碼與使用括號(hào)分組操作相同凸椿,如(vectorA * 2)* vectorB削祈。

3. Protocol Operators

一些運(yùn)算符是協(xié)議的成員。 例如脑漫,符合Equatable的類(lèi)型必須實(shí)現(xiàn)==運(yùn)算符髓抑。 類(lèi)似地,符合Comparable的類(lèi)型必須至少實(shí)現(xiàn)<==优幸,因?yàn)?code>Comparable繼承自Equatable启昧。 Comparable類(lèi)型也可以選擇實(shí)現(xiàn)>> =<=劈伴,但這些運(yùn)算符具有默認(rèn)實(shí)現(xiàn)。

對(duì)于Vector握爷,Comparable并沒(méi)有太多意義跛璧,但Equatable卻很重要,因?yàn)槿绻鼈兊慕M件全部相等新啼,則兩個(gè)向量相等追城。 接下來(lái)你將實(shí)現(xiàn)Equatable

要符合協(xié)議燥撞,請(qǐng)?jiān)?code>playground的末尾添加以下代碼:

extension Vector: Equatable {
  static func == (left: Vector, right: Vector) -> Bool {
    return left.x == right.x && left.y == right.y && left.z == right.z
  }
}

將以下行添加到playground的底部以測(cè)試它:

vectorA == vectorB // false

此行按預(yù)期返回false座柱,因?yàn)?code>vectorA具有與vectorB不同的組件。

符合Equatable不僅能夠檢查這些類(lèi)型的相等性物舒。您還可以獲取矢量數(shù)組的contains(_:)方法色洞!


Creating Custom Operators

還記得我是怎么說(shuō)通常不鼓勵(lì)使用自定義符號(hào)嗎?與往常一樣冠胯,該規(guī)則也有例外火诸。

關(guān)于自定義符號(hào)的一個(gè)好的經(jīng)驗(yàn)法則是,只有在滿足以下條件時(shí)才應(yīng)使用它們:

  • 它們的含義是眾所周知的荠察,或者對(duì)閱讀代碼的人有意義置蜀。
  • 它們很容易在鍵盤(pán)上打字。

您將實(shí)現(xiàn)的最后一個(gè)運(yùn)算符匹配這兩個(gè)條件悉盆。矢量點(diǎn)積產(chǎn)生兩個(gè)向量并返回單個(gè)標(biāo)量數(shù)盯荤。您的運(yùn)算符會(huì)將向量中的每個(gè)值乘以另一個(gè)向量中的對(duì)應(yīng)值,然后將所有這些乘積相加焕盟。

點(diǎn)積的符號(hào)為?秋秤,您可以使用鍵盤(pán)上的Option-8輕松鍵入。

您可能會(huì)想,“我可以在本教程中對(duì)其他所有操作符執(zhí)行相同的操作航缀,對(duì)吧商架?”

不幸的是,你還不能那樣做芥玉。在其他情況下蛇摸,您正在重載已存在的運(yùn)算符。對(duì)于新的自定義運(yùn)算符灿巧,您需要首先創(chuàng)建運(yùn)算符赶袄。

直接在Vector實(shí)現(xiàn)下面,但在CustomStringConvertible一致性擴(kuò)展之上抠藕,添加以下聲明:

infix operator ?: AdditionPrecedence

這將?定義為必須放在兩個(gè)其他值之間的運(yùn)算符饿肺,并且與加法運(yùn)算符+具有相同的優(yōu)先級(jí)。 暫時(shí)忽略優(yōu)先級(jí)別盾似。

既然已經(jīng)注冊(cè)了此運(yùn)算符敬辣,請(qǐng)?jiān)谶\(yùn)算符擴(kuò)展的末尾添加其實(shí)現(xiàn),緊接在乘法和cross-product運(yùn)算符*的實(shí)現(xiàn)之下:

static func ? (left: Vector, right: Vector) -> Int {
  return left.x * right.x + left.y * right.y + left.z * right.z
}

將以下代碼添加到playground的底部以進(jìn)行測(cè)試:

vectorA ? vectorB // 15

到目前為止零院,一切看起來(lái)都不錯(cuò)......或者是嗎溉跃? 在playground的底部嘗試以下代碼:

vectorA ? vectorB + vectorA // Error!

Xcode對(duì)你不滿意。 但為什么告抄?

現(xiàn)在撰茎,?+具有相同的優(yōu)先級(jí),因此編譯器從左到右解析表達(dá)式打洼。 編譯器將您的代碼解釋為:

(vectorA ? vectorB) + vectorA

此表達(dá)式歸結(jié)為Int + Vector龄糊,您尚未實(shí)現(xiàn)并且不打算實(shí)現(xiàn)。 你能做些什么來(lái)解決這個(gè)問(wèn)題募疮?


Precedence Groups

Swift中的所有運(yùn)算符都屬于一個(gè)優(yōu)先級(jí)組(precedence group)炫惩,它描述了運(yùn)算符的計(jì)算順序。 還記得學(xué)習(xí)小學(xué)數(shù)學(xué)中的操作順序嗎阿浓? 這基本上就是你在這里所要處理的诡必。

在Swift標(biāo)準(zhǔn)庫(kù)中,優(yōu)先級(jí)順序如下:

以下是關(guān)于這些運(yùn)算符的一些注釋?zhuān)驗(yàn)槟翱赡軟](méi)有看到它們:

  • 1) 按位移位運(yùn)算符<<>>用于二進(jìn)制計(jì)算搔扁。
  • 2) 您使用轉(zhuǎn)換運(yùn)算符爸舒,isas來(lái)確定或更改值的類(lèi)型。
  • 3) nil合并運(yùn)算符??有助于為可選值提供回退值稿蹲。
  • 4) 如果您的自定義運(yùn)算符未指定優(yōu)先級(jí)扭勉,則會(huì)自動(dòng)分配DefaultPrecedence
  • 5) 三元運(yùn)算符苛聘,涂炎? :忠聚,類(lèi)似于if-else語(yǔ)句。
  • 6) 對(duì)于=的衍生唱捣,AssignmentPrecedence在其他所有內(nèi)容之后進(jìn)行評(píng)估两蟀,無(wú)論如何。

編譯器解析具有左關(guān)聯(lián)性的類(lèi)型震缭,以便v1 + v2 + v3 ==(v1 + v2)+ v3赂毯。 對(duì)于右關(guān)聯(lián)性結(jié)果也是正確的。

操作符按它們?cè)诒碇谐霈F(xiàn)的順序進(jìn)行解析拣宰。 嘗試使用括號(hào)重寫(xiě)以下代碼:

v1 + v2 * v3 / v4 * v5 == v6 - v7 / v8

當(dāng)您準(zhǔn)備好數(shù)學(xué)知識(shí)時(shí)党涕,請(qǐng)查看下面的解決方案。

(v1 + (((v2 * v3) / v4) * v5)) == (v6 - (v7 / v8))

在大多數(shù)情況下巡社,您需要添加括號(hào)以使代碼更易于閱讀膛堤。 無(wú)論哪種方式,理解編譯器評(píng)估運(yùn)算符的順序都很有用晌该。

1. Dot Product Precedence

您的新dot-product并不適合任何這些類(lèi)別肥荔。 它必須少于加法(如前所述),但它是否真的適合CastingPrecedenceRangeFormationPrecedence朝群?

相反次企,您將為您的點(diǎn)積運(yùn)算符創(chuàng)建自己的優(yōu)先級(jí)組。

用以下內(nèi)容替換?運(yùn)算符的原始聲明:

precedencegroup DotProductPrecedence {
  lowerThan: AdditionPrecedence
  associativity: left
}

infix operator ?: DotProductPrecedence

在這里潜圃,您創(chuàng)建一個(gè)新的優(yōu)先級(jí)組并將其命名為DotProductPrecedence。 您將它放在低于AdditionPrecedence的位置舟茶,因?yàn)槟M臃▋?yōu)先谭期。 你也可以將它設(shè)為左關(guān)聯(lián),因?yàn)槟阆胍獜淖蟮接疫M(jìn)行評(píng)估吧凉,就像你在加法和乘法中一樣隧出。 然后,將此新優(yōu)先級(jí)組分配給?運(yùn)算符阀捅。

注意:除了lowerThan之外胀瞪,您還可以在DotProductPrecedence中指定higherThan。 如果您在單個(gè)項(xiàng)目中有多個(gè)自定義優(yōu)先級(jí)組饲鄙,這一點(diǎn)就變得很重要凄诞。

您的舊代碼行現(xiàn)在運(yùn)行并按預(yù)期返回:

vectorA ? vectorB + vectorA // 29

恭喜 - 您已經(jīng)掌握了自定義操作符!

此時(shí)忍级,您知道如何根據(jù)需要重載Swift操作符帆谍。 在本教程中,您專(zhuān)注于在數(shù)學(xué)上下文中使用運(yùn)算符轴咱。 在實(shí)踐中汛蝙,您將找到更多使用運(yùn)算符的方法烈涮。

ReactiveSwift ReactiveSwift framework 框架中可以看到自定義操作符使用的一個(gè)很好的演示。 一個(gè)例子是<~窖剑,這是反應(yīng)式編程中的一個(gè)重要函數(shù)坚洽。 以下是此運(yùn)算符的使用示例:

let (signal, _) = Signal<Int, Never>.pipe()
let property = MutableProperty(0)
property.producer.startWithValues {
  print("Property received \($0)")
}

property <~ signal

Cartography 是另一個(gè)大量使用運(yùn)算符重載的框架。 此AutoLayout工具重載相等和比較運(yùn)算符西土,以使NSLayoutConstraint創(chuàng)建更簡(jiǎn)單:

constrain(view1, view2) { view1, view2 in
  view1.width   == (view1.superview!.width - 50) * 0.5
  view2.width   == view1.width - 50
  view1.height  == 40
  view2.height  == view1.height
  view1.centerX == view1.superview!.centerX
  view2.centerX == view1.centerX

  view1.top >= view1.superview!.top + 20
  view2.top == view1.bottom + 20
}

此外讶舰,您始終可以參考Apple的官方文檔 custom operator documentation

有了這些新的靈感來(lái)源翠储,您可以走出世界绘雁,通過(guò)運(yùn)算符重載使代碼更簡(jiǎn)單。不過(guò)還是要小心使用自定義操作符援所!

下面看下相關(guān)整體代碼

struct Vector {
  let x: Int
  let y: Int
  let z: Int
}

extension Vector: ExpressibleByArrayLiteral {
  init(arrayLiteral: Int...) {
    assert(arrayLiteral.count == 3, "Must initialize vector with 3 values.")
    self.x = arrayLiteral[0]
    self.y = arrayLiteral[1]
    self.z = arrayLiteral[2]
  }
}

precedencegroup DotProductPrecedence {
  lowerThan: AdditionPrecedence
  associativity: left
}

infix operator ?: DotProductPrecedence

extension Vector: CustomStringConvertible {
  var description: String {
    return "(\(x), \(y), \(z))"
  }
}

let vectorA: Vector = [1, 3, 2]
let vectorB: Vector = [-2, 5, 1]

// MARK: - Operators
extension Vector {
  static func + (left: Vector, right: Vector) -> Vector {
    return [
      left.x + right.x,
      left.y + right.y,
      left.z + right.z
    ]
  }
  
  static prefix func - (vector: Vector) -> Vector {
    return [-vector.x, -vector.y, -vector.z]
  }
  
  static func - (left: Vector, right: Vector) -> Vector {
    return left + -right
  }
  
  static func * (left: Int, right: Vector) -> Vector {
    return [
      right.x * left,
      right.y * left,
      right.z * left
    ]
  }
  
  static func * (left: Vector, right: Int) -> Vector {
    return right * left
  }
  
  static func * (left: Vector, right: Vector) -> Vector {
    return [
      left.y * right.z - left.z * right.y,
      left.z * right.x - left.x * right.z,
      left.x * right.y - left.y * right.x
    ]
  }
  
  static func ? (left: Vector, right: Vector) -> Int {
    return left.x * right.x + left.y * right.y + left.z * right.z
  }
}

vectorA + vectorB // (-1, 8, 3)
-vectorA // (-1, -3, -2)
vectorA - vectorB // (3, -2, 1)

extension Vector: Equatable {
  static func == (left: Vector, right: Vector) -> Bool {
    return left.x == right.x && left.y == right.y && left.z == right.z
  }
}

vectorA == vectorB // false

vectorA ? vectorB // 15

vectorA ? vectorB + vectorA // 29

后記

本篇主要講述了重載自定義運(yùn)算符庐舟,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市住拭,隨后出現(xiàn)的幾起案子挪略,更是在濱河造成了極大的恐慌,老刑警劉巖滔岳,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杠娱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谱煤,警方通過(guò)查閱死者的電腦和手機(jī)摊求,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)刘离,“玉大人室叉,你說(shuō)我怎么就攤上這事×蛱瑁” “怎么了茧痕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恼除。 經(jīng)常有香客問(wèn)我踪旷,道長(zhǎng),這世上最難降的妖魔是什么豁辉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任令野,我火速辦了婚禮,結(jié)果婚禮上徽级,老公的妹妹穿的比我還像新娘彩掐。我一直安慰自己,他們只是感情好灰追,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布堵幽。 她就那樣靜靜地躺著狗超,像睡著了一般。 火紅的嫁衣襯著肌膚如雪朴下。 梳的紋絲不亂的頭發(fā)上努咐,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音殴胧,去河邊找鬼渗稍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛团滥,可吹牛的內(nèi)容都是我干的竿屹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼灸姊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拱燃!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起力惯,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤碗誉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后父晶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體哮缺,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年甲喝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尝苇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡埠胖,死狀恐怖糠溜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情押袍,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布凯肋,位于F島的核電站谊惭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侮东。R本人自食惡果不足惜圈盔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悄雅。 院中可真熱鬧驱敲,春花似錦、人聲如沸宽闲。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至娩梨,卻和暖如春沿腰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背狈定。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工颂龙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纽什。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓措嵌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親芦缰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子企巢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容