【r<-高級】R-面向對象編程(一)

內容:

  • S3

借助面向對象的編碼風格塞淹,并加以合理的抽象剪菱,我們可以簡單地模仿對象的重要特性,于是纹笼,問題和模型之間的轉換就變得清晰自然纹份。

S3對象

S3對象系統(tǒng)是一個簡單且寬松的面向對象系統(tǒng)。每個基本對象的類型都有一個S3類名稱廷痘。比如integer,numeric, character, logical, listdata.frame都屬于S3類蔓涧。

舉例,下面vec1類型是double笋额,意味其內部類型或者說存儲模式是雙精度浮點型數(shù)字元暴。但它的類是numeric

vec1 = c(1, 2, 3)typeof(vec1)#> [1] "double"class(vec1)#> [1] "numeric"

下面data1類型是list兄猩,意味data1的內部類型或者存儲模式是列表茉盏,但它的S3類是data.frame

data1 = data.frame(x = 1:3, y = rnorm(3))typeof(data1)#> [1] "list"class(data1)#> [1] "data.frame"

理解對象的內部類型S3類區(qū)別是一個重點枢冤。

一個類可以用多種方法定義它的行為鸠姨,尤其是它與其他類的關系。在S3系統(tǒng)中淹真,我們可以創(chuàng)建泛型函數(shù)(generic function)讶迁,對于不同的類,由泛型函數(shù)決定調用哪個方法核蘸,這就是S3方法分派(method dispatch)的工作機理巍糯。

對象的類不同,其方法分派不同客扎,因此祟峦,區(qū)別對象的類十分重要。

R中有許多基于某個通用目的定義的S3泛型函數(shù)徙鱼,我們先看看head()tail()宅楞。head()展示一個數(shù)據(jù)對象的前n條記錄,tail()展示后n條。這跟x[1:n]是不同的咱筛,因為對不同的類的對象搓幌,記錄的定義是不同的。對原子向量(數(shù)值迅箩、字符向量等)溉愁,前n條記錄指前n個元素。但對于數(shù)據(jù)框饲趋,前n條記錄指前n行而不是前n列拐揭。

查看下head的函數(shù)內部信息:

head#> function (x, ...) #> UseMethod("head")#> <bytecode: 0x0000000018fcb138>#> <environment: namespace:utils>

我們發(fā)現(xiàn)函數(shù)中并沒有實際的操作細節(jié)。它調用UseMethod("head")來讓泛型函數(shù)head()執(zhí)行方法分派奕塑,也就是說堂污,對于不同的類,它可能有不同的執(zhí)行方式(過程)龄砰。

num_vec = c(1, 2, 3, 4, 5)data_frame = data.frame(x = 1:5, y = rnorm(5))

調用函數(shù):

head(num_vec, 3)#> [1] 1 2 3head(data_frame, 3)#>   x     y#> 1 1 0.537#> 2 2 1.072#> 3 3 0.181

我們可以使用methods()查看head()函數(shù)可以實現(xiàn)的所有方法:

methods("head")#> [1] head.data.frame* head.default*    head.ftable*     head.function*  #> [5] head.matrix      head.table*     #> see '?methods' for accessing help and source code

可以看到head不僅僅適用于向量和數(shù)據(jù)框盟猖。

注意,方法都是以method.class形式表示换棚,如果我們輸入一個data.frame式镐,head()會調用head.data.frame方法。當沒有方法可以匹配對象的類時固蚤,函數(shù)會自動轉向method.default方法娘汞。這就是方法分派的一個實際過程。

內置類和方法

S3泛型函數(shù)和方法在統(tǒng)一各個模型的使用方式上是最有用的夕玩。比如我們可以創(chuàng)建一個線性模型你弦,以不同角度查看模型信息:

lm1 = lm(mpg ~ cyl + vs, data = mtcars)

線性模型本質上是由模型擬合產(chǎn)生的數(shù)據(jù)字段構成的列表,所以lm1的類型是list燎孟,但是它的類是lm禽作,因此泛型函數(shù)根據(jù)lm選擇方法:

typeof(lm1)#> [1] "list"class(lm1)#> [1] "lm"

甚至沒有明確調用S3泛型函數(shù)時,S3方法分派也會自動進行缤弦。如果我們輸入lm1

lm1#> #> Call:#> lm(formula = mpg ~ cyl + vs, data = mtcars)#> #> Coefficients:#> (Intercept)          cyl           vs  #>      39.625       -3.091       -0.939

實際上领迈,print()函數(shù)被默默地調用了:

print(lm1)#> #> Call:#> lm(formula = mpg ~ cyl + vs, data = mtcars)#> #> Coefficients:#> (Intercept)          cyl           vs  #>      39.625       -3.091       -0.939

為什么打印出來的不像列表呢?因為print()是一個泛型函數(shù)碍沐,它為lm選擇了一個方法來打印線性模型最重要的信息。我們可以調用getS3method("print", "lm")獲取實際使用的方法與想象的進行驗證:

identical(getS3method("print", "lm"), stats:::print.lm)#> [1] TRUE

print()展示模型的一個簡要版本衷蜓,summary()展示更詳細的信息累提。summary()也是一個泛型函數(shù),它為模型的所有類提供了許多方法:

summary(lm1)#> #> Call:#> lm(formula = mpg ~ cyl + vs, data = mtcars)#> #> Residuals:#>    Min     1Q Median     3Q    Max #> -4.923 -1.953 -0.081  1.319  7.577 #> #> Coefficients:#>             Estimate Std. Error t value Pr(>|t|)    #> (Intercept)   39.625      4.225    9.38  2.8e-10 ***#> cyl           -3.091      0.558   -5.54  5.7e-06 ***#> vs            -0.939      1.978   -0.47     0.64    #> ---#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1#> #> Residual standard error: 3.25 on 29 degrees of freedom#> Multiple R-squared:  0.728,  Adjusted R-squared:  0.71 #> F-statistic: 38.9 on 2 and 29 DF,  p-value: 6.23e-09

實際上磁浇,summary()的輸出結果也是一個對象斋陪,包含的數(shù)據(jù)都可以被訪問。在這個例子里,這個對象是一個列表无虚,是summary.lm類缔赠,它有可供print()選擇的自己的方法:

lm1summary = summary(lm1)typeof(lm1summary)#> [1] "list"class(lm1summary)#> [1] "summary.lm"

查看列表成分:

names(lm1summary)#>  [1] "call"          "terms"         "residuals"     "coefficients" #>  [5] "aliased"       "sigma"         "df"            "r.squared"    #>  [9] "adj.r.squared" "fstatistic"    "cov.unscaled"

還有一些其他有用的且與模型相關的泛型函數(shù),例如plot(),predict()友题。不同的內置模型和第三方擴展包提供的模型都能實現(xiàn)這些泛型函數(shù)嗤堰。

舉例,我們可以對線性模型調用plot()函數(shù):

oldpar = par(mfrow = c(2, 2))plot(lm1)

img

par(oldpar)

為避免依次生成這4個圖度宦,我們用par()將繪圖區(qū)域劃分為2x2的子區(qū)域踢匣。

利用predict()我們可以使用模型對新數(shù)據(jù)進行預測,泛型函數(shù)predict()自動選擇正確的方法用新數(shù)據(jù)進行預測:

predict(lm1, data.frame(cyl = c(6, 8), vs = c(1, 1)))#>    1    2 #> 20.1 14.0

這個函數(shù)既可以用在樣本內戈抄,又可以用在樣本外离唬。如果我們?yōu)槟P吞峁┬聰?shù)據(jù),它就進行樣本外預測划鸽。

下面我們創(chuàng)建一幅真實值和擬合值的散點圖输莺,看一看線性模型的預測效果:

plot(mtcars$mpg, fitted(lm1))

img

這里的fitted()也是泛型函數(shù),等價于lm1$fitted.values裸诽,擬合值等于用原始數(shù)據(jù)得到的預測值模闲,即用原始數(shù)據(jù)構建的模型預測原始數(shù)據(jù),predict(lm1, mtcars)崭捍。

真實值與擬合值的差稱為殘差尸折,可以通過另一個泛型函數(shù)residuals()獲得。

plot(density(residuals(lm1)),     main = "Density of lm1 residuals")

img

這些泛型函數(shù)不僅適用于lm殷蛇、glm和其他內置模型实夹,也適用于其他擴展包提供的模型。

例如我們使用rpart包粒梦,使用前面的數(shù)據(jù)和公式擬合一個回歸樹模型亮航。

if(!require("rpart")) install.packages("rpart")#> 載入需要的程輯包:rpartlibrary(rpart)
tree_model = rpart(mpg ~ cyl + vs, data = mtcars)

我們之所以能夠使用相同的方法,是因為這個包的作者希望函數(shù)調用的方式與調用R內置函數(shù)保持一致匀们。

typeof(tree_model)#> [1] "list"class(tree_model)#> [1] "rpart"

打印模型:

print(tree_model)#> n= 32 #> #> node), split, n, deviance, yval#>       * denotes terminal node#> #> 1) root 32 1130.0 20.1  #>   2) cyl>=5 21  198.0 16.6  #>     4) cyl>=7 14   85.2 15.1 *#>     5) cyl< 7 7   12.7 19.7 *#>   3) cyl< 5 11  203.0 26.7 *

更詳細信息:

summary(tree_model)#> Call:#> rpart(formula = mpg ~ cyl + vs, data = mtcars)#>   n= 32 #> #>       CP nsplit rel error xerror   xstd#> 1 0.6431      0     1.000  1.089 0.2579#> 2 0.0893      1     0.357  0.432 0.0811#> 3 0.0100      2     0.268  0.427 0.0818#> #> Variable importance#> cyl  vs #>  65  35 #> #> Node number 1: 32 observations,    complexity param=0.643#>   mean=20.1, MSE=35.2 #>   left son=2 (21 obs) right son=3 (11 obs)#>   Primary splits:#>       cyl < 5   to the right, improve=0.643, (0 missing)#>       vs  < 0.5 to the left,  improve=0.441, (0 missing)#>   Surrogate splits:#>       vs < 0.5 to the left,  agree=0.844, adj=0.545, (0 split)#> #> Node number 2: 21 observations,    complexity param=0.0893#>   mean=16.6, MSE=9.45 #>   left son=4 (14 obs) right son=5 (7 obs)#>   Primary splits:#>       cyl < 7   to the right, improve=0.507, (0 missing)#>   Surrogate splits:#>       vs < 0.5 to the left,  agree=0.857, adj=0.571, (0 split)#> #> Node number 3: 11 observations#>   mean=26.7, MSE=18.5 #> #> Node number 4: 14 observations#>   mean=15.1, MSE=6.09 #> #> Node number 5: 7 observations#>   mean=19.7, MSE=1.81

下面對結果進行可視化缴淋,得到樹圖:

oldpar = par(xpd = NA)plot(tree_model)text(tree_model, use.n = TRUE)

img

par(oldpar)

為現(xiàn)有類定義泛型函數(shù)

在定義泛型函數(shù)時,我們創(chuàng)建一個函數(shù)去調用UseMethod()出發(fā)方法分派泄朴。然后對泛型函數(shù)想要作用的類創(chuàng)建帶有method.class形式的方法函數(shù)重抖,同時還要創(chuàng)建帶有method.default形式的默認方法來應對所有其他情況

下面我們創(chuàng)建一個新的泛型函數(shù)generic_head()祖灰,它有兩個參數(shù):輸入對象x和需要提取的記錄條數(shù)n钟沛。泛型函數(shù)僅僅調用UseMethod("generic_head")來讓R根據(jù)輸入對象x的類執(zhí)行方法分派。

generic_head = function(x, n)    UseMethod("generic_head")

對原子向量提取前n個元素局扶,因此分別定義generic_head.numeric恨统、generic_head.character等叁扫,另外最好定義一個默認方法捕獲不能匹配的其他所有情況:

generic_head.default = function(x, n){    x[1:n]}

現(xiàn)在generic_head只有一種方法,等于沒有使用泛型函數(shù):

generic_head(num_vec, 3)#> [1] 1 2 3

現(xiàn)在我們還沒有定義針對data.frame類的方法畜埋,所以當我們輸入數(shù)據(jù)框時莫绣,函數(shù)會自動轉向generic_head.default,又因為提取的數(shù)量超出列數(shù)悠鞍,所以下面的運行會報錯:

generic_head(data_frame, 3)#> Error in `[.data.frame`(x, 1:n): 選擇了未定義的列

下面為data.frame定義方法:

generic_head.data.frame = function(x, n) {    x[1:n, ]}

現(xiàn)在函數(shù)就可以正常運行了:

generic_head(data_frame, 3)#>   x     y#> 1 1 0.537#> 2 2 1.072#> 3 3 0.181

因為沒有對參數(shù)進行檢查对室,所以S3類執(zhí)行的方法并不穩(wěn)健。

定義新類并創(chuàng)建對象

現(xiàn)在我們來嘗試構建新類狞玛,class(x)獲取x的類软驰,而class(x) = some_classx的類設為some_class稠集。

使用列表作為底層數(shù)據(jù)結構

列表可能是創(chuàng)建新類時使用最廣泛的數(shù)據(jù)結構辫红,類描述了對象的類型和對象交互作用的方法衍菱,其中對象用于存儲多種多樣几苍、長度不一的數(shù)據(jù)协怒。

下面我們定義一個叫product的函數(shù)力细,創(chuàng)建一個由name宜雀、priceinventory構成的列表本橙,該列表的類是product固该。我們還將自己定義它的print方法锅减。

productor = function(name, price, inventory){    obj = list(name = name,               price = price,               inventory = inventory)    class(obj) = "product"    obj}

上面我們創(chuàng)建了一個列表,然后將它的類替換為product伐坏。我們還可以使用structure()

product = function(name, price, inventory){    structure(list(name = name,              price = price,              inventory = inventory),              class = "product")}

現(xiàn)在我們調用product()函數(shù)生成product類的實例:

laptop = product("Laptop", 499, 300)

查看它的結構和S3類方法分派:

typeof(laptop)#> [1] "list"class(laptop)#> [1] "product"

此時我們還沒有為該類定義任何方法怔匣,如果print將按默認列表輸出:

print(laptop)#> $name#> [1] "Laptop"#> #> $price#> [1] 499#> #> $inventory#> [1] 300#> #> attr(,"class")#> [1] "product"

下面我們自定義一個print方法,使得輸出更緊湊:

print.product = function(x, ...){    cat("<product>\n")    cat("name:", x$name, "\n")    cat("price:", x$price, "\n")    cat("inventory:", x$inventory, "\n")    invisible(x)}

print方法返回輸入對象本身以備后用桦沉,這是一項約定每瞒。

現(xiàn)在我們再來看看輸出:

laptop#> <product>#> name: Laptop #> price: 499 #> inventory: 300

我們可以像操作列表一樣訪問laptop的成分:

laptop$name#> [1] "Laptop"laptop$price#> [1] 499laptop$inventory#> [1] 300

如果我們創(chuàng)建另一個對象,并將兩者放入一個列表然后打印纯露,print.product仍然會被調用:

cellphone = product("Phone", 249, 12000)products = list(laptop, cellphone)products#> [[1]]#> <product>#> name: Laptop #> price: 499 #> inventory: 300 #> #> [[2]]#> <product>#> name: Phone #> price: 249 #> inventory: 12000

products以列表形式被打印時剿骨,會對每個元素調用print()泛型函數(shù),再由泛型函數(shù)執(zhí)行方法分派埠褪。

大多數(shù)其他編程語言都對類有正式的定義浓利,而S3沒有,所以創(chuàng)建一個S3對象比較簡單钞速,但我們需要對輸入?yún)?shù)進行充分的檢查贷掖,以確保創(chuàng)建的對象與所屬類內部一致

除了定義新類玉工,我們還可以定義新的泛型函數(shù)羽资。下面創(chuàng)建一個叫value的泛型函數(shù),它通過測量產(chǎn)品的庫存值來為product調用實施方法:

value = function(x, ...)    UseMethod("value")value.default = function(x, ...){    stop("Value is undefined")}value.product = function(x, ...){    x$price * x$inventory}

針對其他類遵班,value調用default方法并終止運行屠升。

value(laptop)#> [1] 149700value(cellphone)#> [1] 2988000value(data_frame)#> Error in value.default(data_frame): Value is undefined

使用原子向量作為底層數(shù)據(jù)結構

上面我們已經(jīng)演示了創(chuàng)建S3類和泛型函數(shù)的過程,有時候我們需要使用原子向量創(chuàng)建新類狭郑,下面展示百分比形式向量創(chuàng)建過程腹暖。

首先定義一個percent函數(shù),它檢查輸入是否是數(shù)值向量并將輸入對象類型改為percent翰萨,percent類繼承numeric類:

percent = function(x){    stopifnot(is.numeric(x))    class(x) = c("percent", "numeric")    x}

這里的繼承指方法分派首先在percent類中方法找脏答,找不到就去numeric類方法中找。尋找的順序由類名稱的順序決定亩鬼。

pct = percent(c(0.1, 0.05, 0.25))pct#> [1] 0.10 0.05 0.25#> attr(,"class")#> [1] "percent" "numeric"

現(xiàn)在定義方法殖告,讓percent類以百分比形式存在:

as.character.percent = function(x, ...){    paste0(as.numeric(x) * 100, "%")}

現(xiàn)在我們可以得到字符型了:

as.character(pct)#> [1] "10%" "5%"  "25%"

也可以直接調用as.character()percent提供一個format方法:

format.percent = function(x, ...){    as.character(x, ...)}

format現(xiàn)在有相同的效果:

format(pct)#> [1] "10%" "5%"  "25%"

類似地,我們調用format.percent()percent提供print方法:

print.percent = function(x, ...){    print(format.percent(x), quote = FALSE)}

這里指定quote=FALSE使得打印的格式化字符串更像數(shù)字而非字符串雳锋。

pct#> [1] 10% 5%  25%

注意黄绩,使用算術運算符操作后會自動保持輸出向量類不變:

pct + 0.2#> [1] 30% 25% 45%pct * 0.5#> [1] 5%    2.5%  12.5%

可惜使用其他函數(shù)可能不會保持輸入對象的類,比如sum()玷过、mean()等:

sum(pct)#> [1] 0.4mean(pct)#> [1] 0.133max(pct)#> [1] 0.25min(pct)#> [1] 0.05

為了確保百分比形式保存爽丹,我們對percent類實施一些操作:

sum.percent = function(...){    percent(NextMethod("sum"))}mean.percent = function(x, ...){    percent(NextMethod("mean"))}max.percent = function(...){    percent(NextMethod("max"))}min.percent = function(...){    percent(NextMethod("max"))}

NextMethod("sum")numeric類調用sum()函數(shù),然后再調用percent()函數(shù)將輸出的數(shù)值向量包裝為百分比形式:

sum(pct)#> [1] 40%mean(pct)#> [1] 13.3333333333333%max(pct)#> [1] 25%min(pct)#> [1] 5%

但如果我們組合一個百分比向量和其他數(shù)值型的值辛蚊,percent類又會消失掉粤蝎,我們進行相同的改進:

c.percent = function(x, ...){    percent(NextMethod("c"))}
c(pct, 0.12)#> [1] 10% 5%  25% 12%

dan….我們取子集又會有問題

pct[1:3]#> [1] 0.10 0.05 0.25pct[[2]]#> [1] 0.05

同樣地,我們對[[[函數(shù)進行改造:

`[.percent` = function(x, i) {    percent(NextMethod('['))}`[[.percent` = function(x, i){    percent(NextMethod("[["))}

此時顯示就正常了:

pct[1:3]#> [1] 10% 5%  25%pct[[2]]#> [1] 5%

實現(xiàn)這些方法后袋马,我們可以在數(shù)據(jù)框中使用:

data.frame(id = 1:3, pct)#>   id pct#> 1  1 10%#> 2  2  5%#> 3  3 25%

S3繼承

假設我們想要對一些交通工具初澎,例如汽車、公共汽車和飛機進行建模虑凛。這些交通工具有一些共性碑宴,它們都有名稱、速度卧檐、位置墓懂,而且都可以移動。為了形象化描述它們霉囚,我們定義一個基本類捕仔,稱為vehichle,用于存儲公共部分盈罐,另外定義car榜跌、busairplane這3個子類,它們繼承vehichle盅粪,但具有自定義的行為钓葫。

首先,定義一個函數(shù)來創(chuàng)建vehicle對象票顾,它本質上是一個環(huán)境础浮。我們選擇環(huán)境而不是列表帆调,因為需要用到環(huán)境的引用語義,也就是說豆同,我們傳遞一個對象番刊,然后原地修改它,而不會創(chuàng)建這個對象的副本影锈。因此無論什么位置將對象傳遞給函數(shù)芹务,對象總是指向同一個交通工具

Vehicle = function(class, name, speed) {    obj = new.env(parent = emptyenv())    obj$name = name    obj$speed = speed    obj$position = c(0, 0, 0)    class(obj) = c(class, "vehicle")    obj}

這里的class(obj) = c(class, "vehicle")似乎有點語義不明鸭廷。但前者是基礎函數(shù)枣抱,后者是輸入?yún)?shù),R能夠判斷好辆床。

下面函數(shù)創(chuàng)建繼承vehiclecar佳晶、busairplane的特定函數(shù):

Car = function(...){    Vehicle(class = "car", ...)}Bus = function(...){    Vehicle(class = "bus", ...)}Airplane = function(...){    Vehicle(class = "airplane", ...)}

現(xiàn)在我們可以為每一個子類創(chuàng)建實例:

car = Car("Model-A", 80)bus = Bus("Medium-Bus", 40)airplane = Airplane("Big-Plane", 800)

下面為vehicle提供通用的print方法:

print.vehicle = function(x, ...){    cat(sprintf("<vehicle: %s>\n", class(x)[1]))    cat("name:", x$name, "\n")    cat("speed:", x$speed, "km/h\n")    cat("position:", paste(x$position, collapse = ", "), "\n")}

因為我們定義的3個子類都有了繼承,所以print方法通用:

car#> <vehicle: car>#> name: Model-A #> speed: 80 km/h#> position: 0, 0, 0bus#> <vehicle: bus>#> name: Medium-Bus #> speed: 40 km/h#> position: 0, 0, 0airplane#> <vehicle: airplane>#> name: Big-Plane #> speed: 800 km/h#> position: 0, 0, 0

因為交通工具可以移動佛吓,我們創(chuàng)建一個泛型函數(shù)move來表征這樣的狀態(tài):

move = function(vehicle, x, y, z) {    UseMethod("move")}move.vehicle = function(vehicle, movement) {    if (length(movement) != 3){        stop("All three dimensions must be specified to move a vehicle")    }        vehicle$position = vehicle$position + movement    vehicle}

這里我們將汽車和公共汽車的移動限定在二維平面上宵晚。

move.bus = move.car = function(vehicle, movement) {    if (length(movement) != 2){        stop("This vehicle only supports 2d movement")    }        movement = c(movement, 0)    NextMethod("move")}

這里我們將movement的第3個緯度強制轉換為0,然后調用NextMethod("move")來調用move.vehicle()维雇。

飛機既可以在2維也可以在3維:

move.airplane = function(vehicle, movement) {    if (length(movement) == 2){        movement = c(movement, 0)    }        NextMethod("move")}

下載3種方法都實現(xiàn)了淤刃,進行測試。

move(car, c(1, 2, 3))#> Error in move.car(car, c(1, 2, 3)): This vehicle only supports 2d movement

只能輸入二維吱型,所以提示報錯了逸贾。

move(car, c(1, 2))#> <vehicle: car>#> name: Model-A #> speed: 80 km/h#> position: 1, 2, 0
move(airplane, c(1, 2))#> <vehicle: airplane>#> name: Big-Plane #> speed: 800 km/h#> position: 1, 2, 0

飛機,3維:

move(airplane, c(20,100,50))#> <vehicle: airplane>#> name: Big-Plane #> speed: 800 km/h#> position: 21, 102, 50

注意津滞,airplane的位置是累積的铝侵。因為前面說過,它本質是一個環(huán)境触徐,因此修改move.vehicle()中的position不會創(chuàng)建一個副本再修改咪鲜,而是本地修改!

學習自《R語言編程指南》


內容太多撞鹉,下次學習接下來的內容疟丙。

文章作者 王詩翔

上次更新 2018-08-15

許可協(xié)議 CC BY-NC-ND 4.0

class instance S3 S4

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鸟雏,隨后出現(xiàn)的幾起案子享郊,更是在濱河造成了極大的恐慌,老刑警劉巖孝鹊,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炊琉,死亡現(xiàn)場離奇詭異,居然都是意外死亡又活,警方通過查閱死者的電腦和手機苔咪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門锰悼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悼泌,你說我怎么就攤上這事松捉〖薪纾” “怎么了馆里?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長可柿。 經(jīng)常有香客問我鸠踪,道長,這世上最難降的妖魔是什么复斥? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任营密,我火速辦了婚禮,結果婚禮上目锭,老公的妹妹穿的比我還像新娘评汰。我一直安慰自己,他們只是感情好痢虹,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布被去。 她就那樣靜靜地躺著,像睡著了一般奖唯。 火紅的嫁衣襯著肌膚如雪惨缆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天丰捷,我揣著相機與錄音坯墨,去河邊找鬼。 笑死病往,一個胖子當著我的面吹牛捣染,可吹牛的內容都是我干的。 我是一名探鬼主播停巷,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼耍攘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叠穆?” 一聲冷哼從身側響起少漆,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎硼被,沒想到半個月后示损,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡嚷硫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年检访,在試婚紗的時候發(fā)現(xiàn)自己被綠了始鱼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡脆贵,死狀恐怖医清,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情卖氨,我是刑警寧澤会烙,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站筒捺,受9級特大地震影響柏腻,放射性物質發(fā)生泄漏。R本人自食惡果不足惜系吭,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一五嫂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肯尺,春花似錦沃缘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逾滥,卻和暖如春峰档,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寨昙。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工讥巡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舔哪。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓欢顷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捉蚤。 傳聞我的和親對象是個殘疾皇子抬驴,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容