[elixir! #0023] 引擎盖下, `IO.puts` 如何运作
`IO.puts "Hello world!"
接触elixir时, 学会的第一行代码是 IO.puts "Hello world!"
. 出于好奇, 我观察了一下 IO.puts
函数的实现.
IO
我们先到elixir源文件中的IO模块里看看.
IO 模块中 IO.puts
的定义
@doc """Writes `item` to the given `device`, similar to `write/2`,but adds a newline at the end."""@spec puts(device, chardata | String.Chars.t) :: :okdef puts(device \\ :stdio, item) do:io.put_chars map_dev(device), [to_chardata(item), ?\n]end
首先看它的类型, 这里我们需要先了解一点, 在erlang和elixir中, 字符串的类型是不同的.
比如 erlang 中 [97]
等同于 "a"
, 而 elixir 中 <<97>>
等同于 "a"
.
类型 device
, chardata
的定义
@type device :: atom | pid@type chardata() :: :unicode.chardata()
定义内联函数 map_dev/1
, to_chardata/1
.
内联函数的作用是在编译时, 调用内联函数的地方会被替换成该函数的函数体.
@compile {:inline, map_dev: 1, to_chardata: 1}# Map the Elixir names for standard IO and error to Erlang namesdefp map_dev(:stdio), do: :standard_iodefp map_dev(:stderr), do: :standard_errordefp map_dev(other) when is_atom(other) or is_pid(other) or is_tuple(other), do: otherdefp to_chardata(list) when is_list(list), do: listdefp to_chardata(other), do: to_string(other)
Kernel
Kernel 模块中 to_string/1
函数的定义
@doc """Converts the argument to a string according to the`String.Chars` protocol.This is the function invoked when there is string interpolation.## Examplesiex> to_string(:foo)"foo""""defmacro to_string(arg) doquote do: String.Chars.to_string(unquote(arg))end
String.Chars
protocol 对不同类型的参数的实现
import Kernel, except: [to_string: 1]defprotocol String.Chars do@moduledoc ~S"""The `String.Chars` protocol is responsible forconverting a structure to a binary (only if applicable).The only function required to be implemented is`to_string` which does the conversion.The `to_string/1` function automatically importedby `Kernel` invokes this protocol. Stringinterpolation also invokes `to_string` in itsarguments. For example, `"foo#{bar}"` is the sameas `"foo" <> to_string(bar)`."""def to_string(term)
enddefimpl String.Chars, for: Atom dodef to_string(nil) do""enddef to_string(atom) doAtom.to_string(atom)end
enddefimpl String.Chars, for: BitString dodef to_string(term) when is_binary(term) dotermenddef to_string(term) doraise Protocol.UndefinedError,protocol: @protocol,value: term,description: "cannot convert a bitstring to a string"end
enddefimpl String.Chars, for: List dodef to_string(charlist), do: List.to_string(charlist)
enddefimpl String.Chars, for: Integer dodef to_string(term) doInteger.to_string(term)end
enddefimpl String.Chars, for: Float dodef to_string(term) doIO.iodata_to_binary(:io_lib_format.fwrite_g(term))end
end
注意这里使用了一个神奇的 erlang 函数 -- :io_lib_format.fwrite_g
用来转换 Float, 我看了一下 erlang 的源代码, 发现了一段注释
%% Writes the shortest, correctly rounded string that converts
%% to Float when read back with list_to_float/1.
%%
%% See also "Printing Floating-Point Numbers Quickly and Accurately"
%% in Proceedings of the SIGPLAN '96 Conference on Programming
%% Language Design and Implementation.
具体的实现挺复杂的, 有兴趣的朋友可以去看看 https://github.com/erlang/otp...
IO.iodata_to_binary/1
的定义
@doc """Converts iodata (a list of integers representing bytes, listsand binaries) into a binary.The operation is Unicode unsafe.Notice that this function treats lists of integers as raw bytesand does not perform any kind of encoding conversion. If you wantto convert from a charlist to a string (UTF-8 encoded), pleaseuse `chardata_to_string/1` instead.If this function receives a binary, the same binary is returned.Inlined by the compiler.## Examplesiex> bin1 = <<1, 2, 3>>iex> bin2 = <<4, 5>>iex> bin3 = <<6>>iex> IO.iodata_to_binary([bin1, 1, [2, 3, bin2], 4 | bin3])<<1, 2, 3, 1, 2, 3, 4, 5, 4, 6>>iex> bin = <<1, 2, 3>>iex> IO.iodata_to_binary(bin)<<1, 2, 3>>"""@spec iodata_to_binary(iodata) :: binarydef iodata_to_binary(item) do:erlang.iolist_to_binary(item)end
总结
内联函数的作用是在编译时, 调用内联函数的地方会被替换成该函数的函数体, 适用于短小的函数. 定义的内联函数的方法是使用模块属性
@compile {:inline, fun_a: 1, fun_b: 1}
.使用
defprotocal
来定义协议. 使用defimpl .., for: ..
来为某种数据类型实现协议.quote do:
中间没有逗号
[elixir! #0023] 引擎盖下, `IO.puts` 如何运作相关推荐
- 规则引擎 设计 git_引擎盖下的Git
规则引擎 设计 git by Wassim Chegham 由Wassim Chegham 引擎盖下的Git (Git under the hood) Let's explore some commo ...
- Linux下IO编程(一)
Linux下IO编程(一) Linux下IO编程 文件IO open函数--打开or创建一个文件 write函数 read函数 lseek函数 Linux下IO编程 文件IO open函数--打开or ...
- 详解PPP模式下的产业投资基金运作【基金管理】
详解PPP模式下的产业投资基金运作[基金管理] 点击标题下「搏实资本」可快速关注 搏实资本 研究型的投资机构,实操型的专家团队 ﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌ 一.产业投资基金概述 1.产业投资基 ...
- linux环境下IO的常用函数
I/O input&output,是一切实现的基础.如果没有i/o,我们在linux上编译的代码将只会在终端出现,将当前进程关闭后,数据无法保留. 标准IO与系统调用(文件)IO的区别 系统I ...
- linux下IO口模拟I2C的一些总结
2019独角兽企业重金招聘Python工程师标准>>> 以前一直在用I2C接口,因为总是有线程的例子就一直没有去深入的了解,今天分析了一下在linux下通用GPIO模拟I2C的程序. ...
- linux 监控命令iostat,Linux下 IO实时监控iostat命令详解
Linux系统中的iostat是I/O statistics(输入/输出统计)的缩写,iostat工具将对系统的磁盘操作活动进行监视.它的特点是汇报磁盘活动统计情况,同时也会汇报出CPU使用情况.同v ...
- Linux下IO多路复用之select函数的使用
select函数的作用: 如果我们的程序里有两个需要阻塞的地方,例如要从服务器读数据,同时还要从键盘上读数据(若不采用阻塞而用查询的方式则大量占用系统资源).这个时候我们就有两处阻塞,你当然可以用多线 ...
- Linux下IO监控与分析 --转载
转自 https://www.cnblogs.com/quixotic/p/3258730.html 近期要在公司内部做个Linux IO方面的培训, 整理下手头的资料给大家分享下 各种IO监视工具在 ...
- 供应链环境下,制造企业物流运作模式该如何选择
随着经济全球化的快速发展,市场竞争已由单个企业之间的竞争逐渐转变为供应链之间的竞争,企业要想获得竞争优势,必须实施供应链战略资源整合.作为一种新的管理思想和管理方法,供应链管理是通过管控(计划.组织. ...
最新文章
- 一起学spring--依赖注入---简单粗暴的例子展示
- 简单易用的倒计时js代码
- python一些常用方法_python常用的一些技巧
- Image Lab 6 for MacOS WIN 图像分析软件下载
- 国内外ACM/ICPC的OJ,BBS列表
- soidworks 生成PCD点云文件
- (转) Arcgis4js实现链家找房的效果
- leetcode947. Most Stones Removed with Same Row or Column
- 转载:【Oracle 集群】RAC知识图文详细教程(四)--缓存融合技术和主要后台进程
- 泰山游记:所为非风光,为历史尔
- php怎么做性格测试题目,PHP和原生JS实现九型人格在线测试(144题)
- 阿里巴巴大数据计算平台MaxCompute全套攻略
- 一文详尽移动互联网广告监测与归因
- 利用一阶谓词逻辑求解猴子摘香蕉问题
- 计算机与地震论文,地震勘探学术论文
- SOA实施妙方 以CRM为切入点
- Gauss消元法(特解与通解)
- 推荐三大文献检索下载网站,超级实用!重点是免费
- 基于SSM框架的图书馆借阅管理系统
- C语言正余弦函数定点查表算法原理及实现