在本篇文章中, 我们将运用 elixir 的 Pattern matching 来实现: function parse。

def parse(request) do
    # TODO: Parse the request string into a map:

end

首先, 我们回顾下 request:

request = """
GET /wildthings HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*

"""

我们将运用 String.split/2 、List 与 Pattern matching, 从 request 中获得我们所需的:

  • 方法: GET
  • 路径: /wildthings

我们先看看 String.split/2 与 List 的用法:

  • 将 request 依 “\n” 进行划分, 并将划分后的字串, 放入 List lines 中:
iex(2)> lines = String.split(request, "\n")
["GET /wildthings HTTP/1.1", "Host: example.com",
 "User-Agent: ExampleBrowser/1.0", "Accept: */*", "", ""]
  • 我们将能很容易的获取到 List lines 中的各个 String。例如: 取得 List lines 中的第一个 String: “GET /wildthings HTTP/1.1″。
iex(3)> first_line = List.first(lines)
"GET /wildthings HTTP/1.1"
  • 将上述的: “request 依 “\n” 进行划分” 与 “取得 List lines 中的第一个 String”, 用 pipeline 给搞定 (简洁的代码是最重要的)
iex(4)> first_line = request |> String.split("\n") |> List.first
"GET /wildthings HTTP/1.1"
  • 再接再厉 ! 将 List first_line 依 ” ” 划分, 并将划分后的 String, 放入 List parts 中:
parts = String.split(first_line, " ")
["GET", "/wildthings", "HTTP/1.1"]

String.split/2 与 List 的用法就先交流到这里。

接下来, 我们来谈谈 Pattern matching。

Pattern matching; 以等号 (=)  來表示; 同时代表著两个操作: bind 断言 match。

  • 将等号的右边 bind 到等号的左边。
  • 断言等号的左边是否与等号的右边 match?

举个最简单的例子:

  • 将等号右边的 1 bind 到等号左边的 a; a 的值就是 1:
iex(6)> a = 1
1
iex(7)> a
1
  • 断言等号左边的 1 是否与等号右边的 a 是 match 的 ? 当然是 match 的。
iex(8)> 1 = a
1
  • 断言等号左边的 2 是否与等号右边的 a 是 match 的 ? 当然是不 match 的。
iex(9)> 2 = a
** (MatchError) no match of right hand side value: 1

我们再来看看 List 的 Pattern matching:

  • 等号右边的 List [1, 2, 3] bind 等号左边的 List [first, 2, last]; 使得 first = 1, last = 3。
  • 等号左边的 List [first, 2, last] matches 等号右边的 List [1, 2, 3]。
iex(11)> [first, 2, last] = [1,2,3]
[1, 2, 3]
iex(12)> first
1
iex(13)> last
3
iex(14)> 
  • 等号左边的 List [first, 7, last] 不 matches 等号右边的 List [1, 2, 3]。
iex(14)> [first, 7, last] = [1,2,3]
** (MatchError) no match of right hand side value: [1, 2, 3]
  • 等号左边的 List [first, last] 不 matches 等号右边的 List [1, 2, 3]。
iex(14)> [first,last] = [1,2,3]    
** (MatchError) no match of right hand side value: [1, 2, 3]

小总结下 Pattern matching:

  • Pattern matching 可以使我们获取值; 将等号的右边 bind 到等号的左边。
  • Pattern matching 可以使我们断言等号两边的 “值” 或 “结构” 是否一致 (Match) ?

交流完了 Pattern matching, 我们再回到 String first_line:

iex(14)> first_line
"GET /wildthings HTTP/1.1"

我们也再回顾下, String.split/2, 将回传 List:

iex(15)> String.split(first_line, " ")
["GET", "/wildthings", "HTTP/1.1"]

所以, 我们将能很容易的运用 Pattern matching 就能获得:

  • 方法: GET
  • 路径: /wildthings
iex(16)> [method, path, version] = String.split(first_line, " ")
["GET", "/wildthings", "HTTP/1.1"]
iex(17)> method
"GET"
iex(18)> path
"/wildthings"
iex(19)> version
"HTTP/1.1"

当然, 我们不需要 version, 所以, 我们可以写成:

iex(20)> [method, path, _] = String.split(first_line, " ")      
["GET", "/wildthings", "HTTP/1.1"]
iex(21)> method
"GET"
iex(22)> path
"/wildthings"

终于交流完了, String.split/2 、 List、Pattern matching。

我们可以将我们交流的内容, 写回 function parse。

def parse(request) do
    # TODO: Parse the request string into a map:
    first_line = request |> String.split("\n") |> List.first
    [method, path, _] = String.split(first_line, " ")
end

将 method, path 写入 key:value 的 map conv 中:

def parse(request) do
    # TODO: Parse the request string into a map:
    first_line = request |> String.split("\n") |> List.first
    [method, path, _] = String.split(first_line, " ")
    conv = %{ method: method, path: path, resp_body: ""}
 end

最后, 我们要将代码变得简洁; 我们只要简洁的代码

  • 去掉 conv; elixir 会自动将 function parse 最后一行的 Expression, 其所执行的结果, 当成是回传值。
def parse(request) do
    # TODO: Parse the request string into a map:
    first_line = request |> String.split("\n") |> List.first
    [method, path, _] = String.split(first_line, " ")
    %{ method: method, path: path, resp_body: ""}
end
  • 再运用 pipeline, pattern matching; 传说中的简洁代码就出现了。
def parse(request) do
    # TODO: Parse the request string into a map:
    [method, path, _] =
    request
    |> String.split("\n")
    |> List.first
    |> String.split(" ") 

    %{ method: method, path: path, resp_body: ""}
 end

样例代码如下:

defmodule Servy.Handler do
  def handle(request) do
    request
    |> parse
    |> route
    |> format_response

  end

  def parse(request) do
    # TODO: Parse the request string into a map:
    [method, path, _] =
    request
    |> String.split("\n")
    |> List.first
    |> String.split(" ")
    
    %{ method: method, path: path, resp_body: ""}
  end

  def route(conv) do
    # TODO: Create a new map that also has the response body:

  end

  def format_response(conv) do
    # TODO: Use values in the map to create an HTTP response string:

  end

end


request = """
GET /wildthings HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*

"""

response = Servy.Handler.handle(request)
IO.puts response

elixir 的 pipeline 是有结构的; 上、下层之分或者说是有主 pipeline, 副 pipeline。

  • function handle 的 pipeline 就可以说是个主 pipeline; 这个主 pipeline 会 “流” 到 function parse。
  • function parse 内又有另外的 pipeline, 因而构成了一个副 pipeline。

elixir 中将会大量的使用 pattern matching; 毫无疑问的 pattern matching 将使得我们可正式的告别 if then else, 而使得代码更加的简洁。

在后续的文章中, 将有更多关于 pattern matching 的应用。

One thought on “云端分布式架构下的编程语言: elixir; pattern matching”

  1. 感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/xsxyl5 欢迎点赞支持!使用开发者头条 App 搜索 387806 即可订阅《Cloud Native 产品级敏捷》

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据