`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` 如何运作相关推荐

  1. 规则引擎 设计 git_引擎盖下的Git

    规则引擎 设计 git by Wassim Chegham 由Wassim Chegham 引擎盖下的Git (Git under the hood) Let's explore some commo ...

  2. Linux下IO编程(一)

    Linux下IO编程(一) Linux下IO编程 文件IO open函数--打开or创建一个文件 write函数 read函数 lseek函数 Linux下IO编程 文件IO open函数--打开or ...

  3. 详解PPP模式下的产业投资基金运作【基金管理】

    详解PPP模式下的产业投资基金运作[基金管理] 点击标题下「搏实资本」可快速关注 搏实资本 研究型的投资机构,实操型的专家团队 ﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌ 一.产业投资基金概述 1.产业投资基 ...

  4. linux环境下IO的常用函数

    I/O input&output,是一切实现的基础.如果没有i/o,我们在linux上编译的代码将只会在终端出现,将当前进程关闭后,数据无法保留. 标准IO与系统调用(文件)IO的区别 系统I ...

  5. linux下IO口模拟I2C的一些总结

    2019独角兽企业重金招聘Python工程师标准>>> 以前一直在用I2C接口,因为总是有线程的例子就一直没有去深入的了解,今天分析了一下在linux下通用GPIO模拟I2C的程序. ...

  6. linux 监控命令iostat,Linux下 IO实时监控iostat命令详解

    Linux系统中的iostat是I/O statistics(输入/输出统计)的缩写,iostat工具将对系统的磁盘操作活动进行监视.它的特点是汇报磁盘活动统计情况,同时也会汇报出CPU使用情况.同v ...

  7. Linux下IO多路复用之select函数的使用

    select函数的作用: 如果我们的程序里有两个需要阻塞的地方,例如要从服务器读数据,同时还要从键盘上读数据(若不采用阻塞而用查询的方式则大量占用系统资源).这个时候我们就有两处阻塞,你当然可以用多线 ...

  8. Linux下IO监控与分析 --转载

    转自 https://www.cnblogs.com/quixotic/p/3258730.html 近期要在公司内部做个Linux IO方面的培训, 整理下手头的资料给大家分享下 各种IO监视工具在 ...

  9. 供应链环境下,制造企业物流运作模式该如何选择

    随着经济全球化的快速发展,市场竞争已由单个企业之间的竞争逐渐转变为供应链之间的竞争,企业要想获得竞争优势,必须实施供应链战略资源整合.作为一种新的管理思想和管理方法,供应链管理是通过管控(计划.组织. ...

最新文章

  1. 一起学spring--依赖注入---简单粗暴的例子展示
  2. 简单易用的倒计时js代码
  3. python一些常用方法_python常用的一些技巧
  4. Image Lab 6 for MacOS WIN 图像分析软件下载
  5. 国内外ACM/ICPC的OJ,BBS列表
  6. soidworks 生成PCD点云文件
  7. (转) Arcgis4js实现链家找房的效果
  8. leetcode947. Most Stones Removed with Same Row or Column
  9. 转载:【Oracle 集群】RAC知识图文详细教程(四)--缓存融合技术和主要后台进程
  10. 泰山游记:所为非风光,为历史尔
  11. php怎么做性格测试题目,PHP和原生JS实现九型人格在线测试(144题)
  12. 阿里巴巴大数据计算平台MaxCompute全套攻略
  13. 一文详尽移动互联网广告监测与归因
  14. 利用一阶谓词逻辑求解猴子摘香蕉问题
  15. 计算机与地震论文,地震勘探学术论文
  16. SOA实施妙方 以CRM为切入点
  17. Gauss消元法(特解与通解)
  18. 推荐三大文献检索下载网站,超级实用!重点是免费
  19. 基于SSM框架的图书馆借阅管理系统
  20. C语言正余弦函数定点查表算法原理及实现

热门文章

  1. 【nosql】NoSql是什么?
  2. 掌握SpringAOP
  3. Linux万兆网络配置
  4. selenium 环境搭建
  5. 怎么把matlab仿真数据压缩,JPEG图像压缩编码及其MATLAB仿真实现(1)
  6. 二叉树为空意味着二叉树_我是怎么调试出来二叉树的遍历(超精彩配图),从此遍历不再愁了...
  7. oracle number 7 5,oracle上机练习6-7及答案
  8. vector容器详细介绍
  9. QT_UDP传输小结
  10. VS对.ini文件的操作