inpsect 可以用 printable binary 形式回傳任何 value。但 elixir 是用什麼方式實作的? 是不是 guard clause
def inspect(value) when is_atom(value), do: ...
def inspect(value) when is_binary(value), do: ...
protocol 允許不同的資料類型用於相同的函數,不同資料型別的相同函數形態會有相同的行為。很像是 behavior,但 behavior 用在 module 裡面,protocol 可在 module 外面實作,這表示我們可以自由擴充 module 不足的功能。
defprotocol 定義 protocol,defprotocol 只定義 function,defimpl 實作要放在不同的地方。
defprotocol Inspect do
def inspect(thing, opts)
end
增加了這兩個實作,就可以 inspect PID
defimpl Inspect, for: PID do
def inspect(pid, _opts) do
"#PID" <> IO.iodata_to_binary(:erlang.pid_to_list(pid))
end
end
defimpl Inspect, for: Reference do
def inspect(ref, _opts) do
'#Ref' ++ rest = :erlang.ref_to_list(ref)
"#Reference" <> IO.iodata_to_binary(rest)
end
end
iex(1)> inspect self()
"#Process<0.89.0>"
可在 for: 後面使用的 Types 有
Any
Atom
BitString
Float
Function
Integer
List
Map
PID
Port
Record
Reference
Tuple
is_collection.exs
defprotocol Collection do
@fallback_to_any true
def is_collection?(value)
end
defimpl Collection, for: [List, Tuple, BitString, Map] do
def is_collection?(_), do: true
end
defimpl Collection, for: Any do
def is_collection?(_), do: false
end
Enum.each [ 1, 1.0, [1,2], {1,2}, %{}, "cat" ], fn value ->
IO.puts "#{inspect value}: #{Collection.is_collection?(value)}"
end
$ elixir is_collection.exs
1: false
1.0: false
[1, 2]: true
{1, 2}: true
%{}: true
"cat": true
Protocol and Structs
Elixir 沒有 classes,但支援 user-defined types
defmodule Blob do
defstruct content: nil
end
iex(1)> c "basic.exs"
[Blob]
iex(2)> b = %Blob{content: 123}
%Blob{content: 123}
iex(3)> inspect b
"%Blob{content: 123}"
## structs 其實是 map,key為 __struct__
iex(4)> inspect b, structs: false
"%{__struct__: Blob, content: 123}"
Built-In Protocols
elixir 有以下內建的 protocols
- Enumerable and Collectable
- Inspect
- List.Chars
- String.Chars
首先定義一個以 0s 1s 表示的 integer
bitmap.exs
defmodule Bitmap do
defstruct value: 0
@doc """
A simple accessor for the 2^bit value in an integer
iex> b = %Bitmap{value: 5}
%Bitmap{value: 5}
iex> Bitmap.fetch_bit(b,2)
1
iex> Bitmap.fetch_bit(b,1)
0
iex> Bitmap.fetch_bit(b,0)
1
"""
def fetch_bit(%Bitmap{value: value}, bit) do
use Bitwise
(value >>> bit) &&& 1
end
end
- Enumerable and Collectable
Enumerable 定義了三個 functions
defprotocol Enumerable do
# collection 的元素數量
def count(collection)
# 是否包含某個 value
def member?(collection, value)
# reduce fun to collection elements
def reduce(collection, acc, fun)
end
針對剛剛的 Bitmap 實作 Enumerable
defimpl Enumerable, for: Bitmap do
import :math, only: [log: 1]
def reduce(bitmap, {:cont, acc}, fun) do
bit_count = Enum.count(bitmap)
_reduce({bitmap, bit_count}, { :cont, acc }, fun)
end
defp _reduce({_bitmap, -1}, { :cont, acc }, _fun), do: { :done, acc }
defp _reduce({bitmap, bit_number}, { :cont, acc }, fun) do
with bit = Bitmap.fetch_bit(bitmap, bit_number),
do: _reduce({bitmap, bit_number-1}, fun.(bit, acc), fun)
end
defp _reduce({_bitmap, _bit_number}, { :halt, acc }, _fun), do: { :halted, acc }
defp _reduce({bitmap, bit_number}, { :suspend, acc }, fun),
do: { :suspended, acc, &_reduce({bitmap, bit_number}, &1, fun), fun }
def member?(value, bit_number) do
{ :ok, 0 <= bit_number && bit_number < Enum.count(value) }
end
def count(%Bitmap{value: value}) do
{ :ok, trunc(log(abs(value))/log(2)) + 1 }
end
end
fifty = %Bitmap{value: 50}
IO.puts Enum.count fifty # => 6
IO.puts Enum.member? fifty, 4 # => true
IO.puts Enum.member? fifty, 6 # => false
IO.inspect Enum.reverse fifty # => [0, 1, 0, 0, 1, 1, 0]
IO.inspect Enum.join fifty, ":" # => "0:1:1:0:0:1:0"
iex(1)> c ("bitmap.exs")
[Bitmap]
iex(2)> c ("bitmap_enumerable.exs")
6
true
false
[0, 1, 0, 0, 1, 1, 0]
"0:1:1:0:0:1:0"
[Enumerable.Bitmap]
defimpl Collectable, for: Bitmap do
use Bitwise
# 回傳 tuple,(1) value (2) function
def into(%Bitmap{value: target}) do
{target, fn
acc, {:cont, next_bit} -> (acc <<< 1) ||| next_bit
acc, :done -> %Bitmap{value: acc}
_, :halt -> :ok
end}
end
end
iex(3)> c("bitmap_collectable.exs")
[Collectable.Bitmap]
iex(4)> Enum.into [1,1,0,0,1,0], %Bitmap{value: 0}
%Bitmap{value: 50}
- Inspect
defmodule Bitmap do
defstruct value: 0
defimpl Inspect do
def inspect(%Bitmap{value: value}, _opts) do
"%Bitmap{#{value}=#{as_binary(value)}}"
end
defp as_binary(value) do
to_string(:io_lib.format("~.2B", [value]))
end
end
end
iex(6)> c("bitmap_inspect.exs")
[Bitmap, Inspect.Bitmap]
iex(7)> fifty = %Bitmap{value: 50}
%Bitmap{50=110010}
iex(8)> inspect fifty
"%Bitmap{50=110010}"
iex(9)> inspect fifty, structs: false
"%{__struct__: Bitmap, value: 50}"
iex(10)> %Bitmap{value: 12345678901234567890}
%Bitmap{12345678901234567890=1010101101010100101010011000110011101011000111110000101011010010}
defmodule Bitmap do
defstruct value: 0
defimpl Inspect, for: Bitmap do
import Inspect.Algebra
def inspect(%Bitmap{value: value}, _opts) do
concat([
nest(
concat([
"%Bitmap{",
break(""),
nest(concat([to_string(value),
"=",
break(""),
as_binary(value)]),
2),
]), 2),
break(""),
"}"])
end
defp as_binary(value) do
to_string(:io_lib.format("~.2B", [value]))
end
end
end
iex(1)> c("bitmap_algebra.exs")
[Bitmap, Inspect.Bitmap]
iex(2)>
nil
iex(3)> big_bitmap = %Bitmap{value: 12345678901234567890}
%Bitmap{12345678901234567890=
1010101101010100101010011000110011101011000111110000101011010010}
iex(4)>
nil
iex(5)> IO.inspect big_bitmap
%Bitmap{12345678901234567890=
1010101101010100101010011000110011101011000111110000101011010010}
%Bitmap{12345678901234567890=
1010101101010100101010011000110011101011000111110000101011010010}
iex(6)> IO.inspect big_bitmap, structs: false
%{__struct__: Bitmap, value: 12345678901234567890}
%Bitmap{12345678901234567890=
1010101101010100101010011000110011101011000111110000101011010010}
- List.Chars & String.Chars
bitmap_string.exs
defimpl String.Chars, for: Bitmap do
def to_string(bitmap) do
import Enum
bitmap
|> reverse
|> chunk(3)
|> map(fn three_bits -> three_bits |> reverse |> join end)
|> reverse
|> join("_")
end
end
iex(1)> c("bitmap.exs")
[Bitmap]
iex(2)> c("bitmap_enumerable.exs")
[Enumerable.Bitmap]
iex(3)> c("bitmap_string.exs")
[String.Chars.Bitmap]
iex(4)> fifty = %Bitmap{value: 50}
%Bitmap{value: 50}
iex(5)> "Fifty in bits is: #{fifty}"
"Fifty in bits is: 110_010"
沒有留言:
張貼留言