Elixir 簡(jiǎn)明筆記(九)--- 匿名函數(shù)

Elixir 是函數(shù)式語言,函數(shù)是 Elixir 的核心,也是一種基本類型。前面我們已經(jīng)知道一些內(nèi)建的函數(shù),例如div,rem,is_list等。與大多數(shù)語言語言,函數(shù)可以調(diào)用,通常使用一對(duì)圓括號(hào),與大多數(shù)語言不一樣,Elixir 的函數(shù)是可以省略圓括號(hào)。

當(dāng)然這個(gè)特性 Ruby也有。Elixr的函數(shù)其實(shí)分為兩種,一種是匿名函數(shù),另外一種則是命名函數(shù)。它們都是函數(shù),卻有著不一樣的地方。我們先了解匿名函數(shù)。

定義

一般的編程語言,函數(shù)的定義都是通過關(guān)鍵字和函數(shù)的名字,或者通過返回類型和函數(shù)名字,有的還有參數(shù)來定義,例如 js和 python

先看js

function func_name(){
    // func_body
}

python的函數(shù)定義

def func_name():
    # func_body
    pass

當(dāng)然有的時(shí)候,js和python也可以這樣定義函數(shù),即函數(shù)表達(dá)式

sayHello = function (){
    return  "hello";
}
調(diào)用
sayHello();

say_hello = lambda : 'hello'
say_hello()

對(duì)于js和python,第一種即函數(shù)的定義,第二種可以稱之為匿名函數(shù),因?yàn)楹瘮?shù)都沒有名字,但是函數(shù)表達(dá)式可以賦值給一個(gè)變量,通過變量調(diào)用函數(shù)。

好了,說了這么多,都是js和python。我們的重點(diǎn)是Elixir啊。幸運(yùn)的,Elixir的匿名函數(shù)和上述兩個(gè)語言極其相識(shí)。

其語法字面大概如下:

fn 
parameter-list -> body
parameter-list -> body
...

end

Elixir的fn...end包裹的函數(shù)體,類似別的js的花括號(hào)包裹的函數(shù)體。定義匿名函數(shù)的時(shí)候,要把函數(shù)表達(dá)式綁定給一個(gè)變量,通過變量調(diào)用。

iex(1)> sum = fn -> "hello" end
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(2)> sum
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(3)> sum.()
"hello"

代碼和js很相似吧,不過elixir的匿名函數(shù)調(diào)用的時(shí)候,不是通過括號(hào)直接調(diào)用,而是在綁定的變量和括號(hào)之間,有一個(gè).操作符。并且括號(hào)不能省略。當(dāng)然匿名函數(shù)的參數(shù)列表的括號(hào)可以省略。

iex(5)> two_number_sum = fn a, b -> a + b end
#Function<12.90072148/2 in :erl_eval.expr/5>
iex(6)> two_number_sum
#Function<12.90072148/2 in :erl_eval.expr/5>
iex(7)> two_number_sum.(1, 2)
3
iex(8)> two_number_sum.1, 2
** (SyntaxError) iex:8: syntax error before: 1

匿名函數(shù)的模式匹配

我們不僅一次說過,Elixir中模式匹配至關(guān)重要。elixir的類型幾乎都可以進(jìn)行模式匹配,函數(shù)也存在模式匹配。

上面的two_number_sum匿名函數(shù)中,a和b兩個(gè)參數(shù),通常稱之為形參,1,2為傳入的參數(shù),通常稱之為實(shí)參。基于以前的經(jīng)驗(yàn),不管a和b是傳值還是傳引用,都可以看出1,2對(duì)a,b進(jìn)行賦值。

實(shí)際上,elixir中并不存在賦值的概念。實(shí)際上是模式匹配。two_number_sum匿名函數(shù)調(diào)用的時(shí)候,參數(shù)可以進(jìn)行模式匹配,當(dāng)傳入1,2的時(shí)候,Elixir會(huì)嘗試用參數(shù)a,b進(jìn)行模式匹配,例如:


{a, b} = {1, 2}

舉一反三,我們會(huì)想到,當(dāng)調(diào)用函數(shù)的時(shí)候,甚至可以使用更復(fù)雜的模式匹配。下面的匿名函數(shù)接受一個(gè)元組參數(shù),然后把元組的元素對(duì)調(diào)位置返回。

iex(1)> swap = fn {a, b} -> {b, a} end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex(2)> swap.({6, 8})
{8, 6}

匿名函數(shù)的參數(shù)列表的括號(hào)是可以省略的,不雅被上面的元組花括號(hào)迷惑哦。

多函數(shù)體

函數(shù)傳參調(diào)用是模式匹配的過程,那么如果不匹配,顯然就無法綁定參數(shù)變量,進(jìn)而執(zhí)行函數(shù)了。因此匹配可以作為匿名函數(shù)執(zhí)行函數(shù)體的一個(gè)條件。實(shí)際上很多情況模式匹配都可以做邏輯的分支處理,即匹配的時(shí)候該怎樣,不匹配的時(shí)候又怎樣。匿名函數(shù)可以模式匹配,那么設(shè)置多個(gè)匹配么?

答案是肯定的,Elixir的匿名函數(shù)可以設(shè)置多個(gè)參數(shù)列表,已經(jīng)參數(shù)列表對(duì)應(yīng)的函數(shù)體。

iex(1)> handle_open = fn
...(1)>   {:ok, file} -> "Read data: # 返回 {IO.read(file, :line)}"
...(1)>   {_, error} -> "Error: # 返回 {:file.format_error(error)}"
...(1)> end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex(2)> handle_open.(File.open("./elixir.txt"))
"Read data: hello elixir"
iex(3)> handle_open.(File.open("./elixir.txts"))
"Error: no such file or directory"

介紹元組的時(shí)候,曾經(jīng)說過使用元組的模式匹配來打開文件。這里面把元組的模式匹配過程分別作為匿名函數(shù)的參數(shù)和函數(shù)體。

仔細(xì)看一下函數(shù)的定義。第二和第三行定義了兩個(gè)不同的函數(shù)體。每一個(gè)函數(shù)體都接受一個(gè)單獨(dú)的元組。第一個(gè)函數(shù)體的參數(shù)元組第一個(gè)元素是:ok。而另外一個(gè)函數(shù)體則是_。這兩個(gè)元組正好可以匹配文件打開成功或者失敗的返回值。

第六行我們將File.open打開存在的文件,并把結(jié)果作為參數(shù)調(diào)用handle_open函數(shù)。這里函數(shù)將會(huì)傳入一個(gè)元組{:ok,file},這個(gè)參數(shù)正好喝第二行的函數(shù)子句模式匹配,匹配的代碼中IO.read將會(huì)讀出文件內(nèi)容。

再一次調(diào)用handle_open。這一串我們嘗試讀取一個(gè)不存在的文件。此時(shí)讀取文件的函數(shù)返回元組是({:error,:enoent}) 。這個(gè)參數(shù)和函數(shù)的第二個(gè)字句匹配。因此會(huì)執(zhí)行第二個(gè)函數(shù)字句的內(nèi)容。

注意上面那段代碼。第三行我們調(diào)用:file.format_error。:file是一個(gè)Erlang文件模塊的引用,也就是調(diào)用了erlang模塊的format_error函數(shù)。這與第六行的File.open調(diào)用不一樣。后者使用了Elixir內(nèi)建的模塊。

該例子正好展示了在Elixir環(huán)境中與Erlang的無縫對(duì)接。你可以在Elixir中使用任何Erlang的庫。Erlang中又很豐富的庫足夠你選擇。當(dāng)然需要注意,畢竟兩個(gè)語言的調(diào)用方式不一樣。后面將會(huì)介紹更多在elixir中使用erlang的庫的內(nèi)容。

上述例子的最后展示了elixir的字符串插入操作。#{變量}定義了字符串格式化占位方式。這里面的占位變量會(huì)被真實(shí)的字符串替換。

可以預(yù)見,匿名函數(shù)的參數(shù)的多個(gè)參數(shù)和函數(shù)體的功能很強(qiáng)大,類似其他語言的函數(shù)重載,即函數(shù)簽名和參數(shù)列表重新定義函數(shù)。但是Elixir通過參數(shù)模式匹配來執(zhí)行對(duì)應(yīng)的函數(shù)體,實(shí)際上類似一個(gè)邏輯分支,實(shí)現(xiàn)Elixir的控制結(jié)構(gòu)。為啥不用ifelse等結(jié)構(gòu)呢?if else確實(shí)是標(biāo)準(zhǔn)的流程控制,可是Elixir中,很少使用它們。模式匹配能夠比它們更好的工作,也更簡(jiǎn)潔。后面再單獨(dú)介紹ifelse等控制結(jié)構(gòu)。

函數(shù)返回函數(shù)

有用動(dòng)態(tài)語言編程經(jīng)驗(yàn)的開發(fā)者也行不會(huì)模式,函數(shù)除了返回值,也可以返回一個(gè)函數(shù)。Elixir的匿名函數(shù)也可以返另外一個(gè)匿名函數(shù)。

iex(1)> func = fn -> fn -> "hello" end end
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(2)> func.()
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(3)> func.().()
"hello"

寫成一行比較難懂,更好的格式化如下:

func = fn ->
  fn ->
    "Hello"
  end
end

變量fun1綁定了一個(gè)函數(shù)。這個(gè)函數(shù)沒有參數(shù),函數(shù)體又定義了一個(gè)新的匿名函數(shù),內(nèi)函也沒有參數(shù),但是返回一個(gè)字符串“Hello”。

調(diào)用func.()調(diào)用外函數(shù)的時(shí)候,返回內(nèi)函數(shù)。然后使用func.().()調(diào)用內(nèi)函數(shù)結(jié)果。

寫func.().()看起來不正常。可以調(diào)用外函數(shù)再綁定一個(gè)新的變量。再通過新的變量去調(diào)用內(nèi)函數(shù):

iex(4)> other = func.()
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(5)> other.()
"hello"

函數(shù)存儲(chǔ)的原始環(huán)境

再看匿名函數(shù)的特性:

iex> greeter = fn name -> (fn -> "Hello #{name}" end) end
#Function<12.17052888 in :erl_eval.expr/5>
iex> dave_greeter = greeter.("Dave")
#Function<12.17052888 in :erl_eval.expr/5>
iex> dave_greeter.()
"Hello Dave"

上面定義了帶有name參數(shù)的外函數(shù)。與所有參數(shù)一樣,函數(shù)體name都是可用的。內(nèi)函數(shù)把name與格式化字符串返回。

當(dāng)我們調(diào)用外函數(shù)的時(shí)候,返回內(nèi)函數(shù)。此時(shí),還沒有把name傳遞到內(nèi)涵上中。但是當(dāng)我再次嗲用內(nèi)涵上的時(shí)候(dave_greeter.())。此時(shí)name將會(huì)替換內(nèi)函數(shù)的格式化占位符。

但是有一些奇怪的事情發(fā)生。內(nèi)函數(shù)使用了外還是的name參數(shù)。在greeter.("Dave")調(diào)用的時(shí)候返回了,此時(shí)外函數(shù)其實(shí)已經(jīng)執(zhí)行完畢了,參數(shù)應(yīng)該離開其作用域了。但是,當(dāng)我們返回內(nèi)涵上的時(shí)候,實(shí)際上還是可以愉快的使用外函數(shù)的參數(shù)。

這樣是可行的,因?yàn)镋lixir的函數(shù)在定義的時(shí)候,就會(huì)自動(dòng)的與作用域的變量進(jìn)行綁定。例子中,當(dāng)我們的定義了內(nèi)函數(shù),它繼承了外函數(shù)作用域的變量綁定。這就是閉包作用域綁定了變量,然后保持這個(gè)狀態(tài)知道被使用。

參數(shù)化函數(shù)

在上一個(gè)例子。外函數(shù)接受一個(gè)參數(shù),而內(nèi)函數(shù)沒有參數(shù)。讓我們嘗試一下不同的例子,即內(nèi)函數(shù)也有參數(shù)的情況:

iex> add_n = fn n -> (fn other -> n + other end) end
#Function<12.17052888 in :erl_eval.expr/5>
iex> add_two = add_n.(2)
#Function<12.17052888 in :erl_eval.expr/5>
iex> add_five = add_n.(5)
#Function<12.17052888 in :erl_eval.expr/5>
iex> add_two.(3)
5
iex> add_five.(7)
12

例子中,內(nèi)函數(shù)使用外函數(shù)的變量n,并和其自身的參數(shù)other相加。每一次我們調(diào)用外函數(shù),就傳遞了一個(gè)n,然后返貨一個(gè)把n當(dāng)成其自己參數(shù)的函數(shù)。

把函數(shù)當(dāng)成參數(shù)傳遞

函數(shù)也是值,因此我們可以把其傳給別的函數(shù)。

iex> times_2 = fn n -> n * 2 end
#Function<12.17052888 in :erl_eval.expr/5>
iex> apply = fn (fun, value) -> fun.(value) end
#Function<12.17052888 in :erl_eval.expr/5>
iex> apply.(times_2, 6)
12

這個(gè)例子中,apply綁定匿名函數(shù),其中第一個(gè)參數(shù)是一個(gè)匿名函數(shù)。它返回第一個(gè)參數(shù)(匿名函數(shù))使用第二個(gè)參數(shù)作為其自身的參數(shù)調(diào)用的結(jié)果。

傳遞函數(shù)將會(huì)讓Elixir代碼無所不能。例如,內(nèi)建的Enum模塊有一個(gè)map函數(shù)。這個(gè)函數(shù)接受兩個(gè)參數(shù),一個(gè)容器集合和一個(gè)參數(shù)函數(shù)。該函數(shù)將容器里的每一個(gè)元素傳入?yún)?shù)函數(shù)進(jìn)行調(diào)用,并將調(diào)用的結(jié)果返回的值放入新容器,并返回新容器。看下面的例子:

iex> list = [1, 3, 5, 7, 9]
[1, 3, 5, 7, 9]
iex> Enum.map list, fn elem -> elem * 2 end
[2, 6, 10, 14, 18]
iex> Enum.map list, fn elem -> elem * elem end
[1, 9, 25, 49, 81]
iex> Enum.map list, fn elem -> elem > 6 end
[false, false, false, true, true]

&符號(hào)

創(chuàng)建簡(jiǎn)短的函數(shù)十分普遍,因此Elixir提供了一個(gè)縮寫的語法糖。下看看例子我們?cè)倮^續(xù):

iex> add_one = &(&1 + 1)        # 等價(jià)于 add_one = fn (n) -> n + 1 end
#Function<6.17052888 in :erl_eval.expr/5>
iex> add_one.(44)
45
iex> square = &(&1 * &1)
#Function<6.17052888 in :erl_eval.expr/5>
iex> square.(8)
64
iex> speak = &(IO.puts(&1))
&IO.puts/1
iex> speak.("Hello")
Hello
:ok

&可以將表達(dá)式轉(zhuǎn)換成函數(shù)。在表達(dá)式中,&1, &2是函數(shù)參數(shù)占位符,分別表示第一個(gè)和第二個(gè)參數(shù)。&(&1 + &2) 可以把表達(dá)式 p1 + p2 轉(zhuǎn)換成 fn p1, p2 -> p1 + p2 end

Elixir很智能。仔細(xì)看前一個(gè)例子speak。Elixir生成了一個(gè)匿名函數(shù),也就是 &(IO.puts(&1))將會(huì)轉(zhuǎn)換成 fn x -> IO.puts(x) end。但是Elixir可以識(shí)別函數(shù)體重有一個(gè)命名函數(shù)---IO模塊的puts函數(shù),其中匿名函數(shù)的第一個(gè)參數(shù)也正是這個(gè)函數(shù)的參數(shù)。Elixir可以使用&符號(hào)做為函數(shù)IO.puts/1的引用來作進(jìn)一步優(yōu)化。

如下的例子,函數(shù)的參數(shù)必須填寫正確的順序:

iex> rnd = &(Float.round(&1, &2))
&Float.round/2
iex> rnd.(5.673, 2)
5.67
iex> rnd = &(Float.round(&2, &1))
#Function<12.17052888 in :erl_eval.expr/5>
iex> rnd.(2, 5.673)
5.67

你可能注意到了定義的函數(shù)的時(shí)候彈出了引用Erlang的信息。因?yàn)?code>Elixir是運(yùn)行在Erlang虛擬機(jī)上的。如果你使用&abs(&1)會(huì)看到更多的證據(jù)。Elixir的引用Erlang庫的絕對(duì)值abs函數(shù),只需要寫&:erlang.abs/1

iex> abs -1
1
iex> abs_fn = &:erlang.abs/1
&:erlang.abs/1
iex> abs_fn.(-1)
1

因?yàn)?code>[]和{}是Elixir中定義列表和元組的操作符,它們也可以轉(zhuǎn)成成函數(shù)。下面的函數(shù)就是繁華了一個(gè)元組,里面有兩個(gè)表達(dá)式,分別對(duì)兩個(gè)參數(shù)做整除和取余操作。

iex> divrem = &{ div(&1,&2), rem(&1,&2) }
#Function<12.17052888 in :erl_eval.expr/5>
iex> divrem.(13, 5)
{2, 3}

&還有另外一種形式。你可以用&和函數(shù)的定義式(函數(shù)名和參數(shù)個(gè)數(shù)的形式,function name/arity)來引用函數(shù),會(huì)返回一個(gè)可調(diào)用的匿名函數(shù)。匿名函數(shù)的參數(shù)將會(huì)被傳到這個(gè)引用函數(shù)中。

我們已經(jīng)知道:當(dāng)我們?cè)趇ex中輸入&(IO.puts(&1))putsIO模塊的函數(shù),它接收一個(gè)參數(shù)。Elixir中它的函數(shù)定義式為IO.puts/1。如果我們?cè)谶@個(gè)表達(dá)式前加上&,我們將會(huì)得到這個(gè)函數(shù)的引用。可以理解為這樣的組合得到了一個(gè)函數(shù)的別名。來看幾個(gè)例子:

iex> l = &length/1   # length/1 是一個(gè)函數(shù),使用&引用length/1 函數(shù),得到一個(gè)length/1 函數(shù)的別名。使用別名調(diào)用該函數(shù)(匿名函數(shù)方式調(diào)用)。
&:erlang.length/1
iex> l.([1,3,5,7])
4
iex> len = &Enum.count/1
&Enum.count/1
iex> len.([1,2,3,4])
4
iex> m = &Kernel.min/2  # Erlang 函數(shù)的別名
&:erlang.min/2
iex> m.(99,88)
88

我們寫了命名函數(shù)的定義式,就像我們定義了函數(shù)一樣。(實(shí)際上并沒有定義函數(shù),而是引用了函數(shù)而已)。

&其實(shí)是匿名函數(shù)的簡(jiǎn)寫,在Elixir中很有用:

iex> Enum.map [1,2,3,4], &(&1 + 1)
[2, 3, 4, 5]
iex> Enum.map [1,2,3,4], &(&1 * &1)
[1, 4, 9, 16]
iex> Enum.map [1,2,3,4], &(&1 < 3)
[true, true, false, false]

函數(shù)是核心

毫無疑問,函數(shù)式Elixir中的核心。函數(shù)編程的基礎(chǔ)是數(shù)據(jù)轉(zhuǎn)換。函數(shù)是轉(zhuǎn)換的引擎,它們就是Elixir的心臟。

目前為止,已經(jīng)介紹了匿名函數(shù),并綁定給變量。其實(shí)函數(shù)也是可以有名字的。

Elixir也支持命名函數(shù)(named function )。Elixir命名函數(shù)定義在模塊內(nèi),對(duì),接下來我們將窺視Elixir的模塊和命名函數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,690評(píng)論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評(píng)論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,438評(píng)論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,667評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,845評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評(píng)論 1 295
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,384評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評(píng)論 2 380

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