zoukankan      html  css  js  c++  java
  • Erlang那些事儿第2回之我是模块(module),一文件一模块

      前几篇文章会写得比较基础,但是既然要写一系列的文章,还是得从基础开始写。我刚学Erlang碰到最大的问题是,想网上搜索下语法,结果却是寥寥无几,而且介绍得不是很系统,对我了解一些细节是有影响的,正好我身边有好多Erlang大神,遇到问题可以随时找他们请教,经过自己消化后,分享到这里,希望可以帮助到一些人。这几天偶尔逛一逛博客园,发现这里真是程序员的知识海洋,随便翻两页,就有很多大佬在编写Java并发、Docker镜像、K8S等技术文章,文章的质量我觉得都可以出书了。虽然我之前经常在CSDN,但是没看过这么专业的,看来程序大佬都在博客园。

      开始聊正题吧,今天聊到是模块(Module),模块就是存放代码的地方。

      C语言有.h头文件和.c源文件,同理,Erlang代码也有这2个玩意儿,只不过后缀有点区别,Erlang的头文件后缀为.hrl,源文件的后缀为.erl。每个Erlang源文件都是一个模块,模块名就是文件名称,每个.erl模块编译后会产生一个.beam文件,就好比.java类编译后会产生一个.class文件。

    知识点1:编写一个Hello World模块

      创建一个文件hello_world.erl,代码如下:

    -module(hello_world).
    -export([hello/0]). hello() -> "Hello Erlang". world() -> "Hello World".

      这个模块非常简单,只有2个函数,分别是hello和world。这里有几个概念,module(模块)、export(函数导出列表)、函数。

      export里面只有hello,说明其它模块只能访问到hello函数,无法访问到world函数。hello类似于Java声明为public公有函数,world类似于private私有函数。

      现在来编译下hello_world模块,并分别执行下2个函数看下返回信息:

    Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
    
    Eshell V11.1.3  (abort with ^G)
    1> ls(). %% ls()函数在终端显示当前目录下的所有文件,输入help().可查看所有命令
    hello_world.erl
    ok
    2> c(hello_world). %% c()函数在终端编译hello_world模块,注意不能加.erl后缀
    hello_world.erl:18: Warning: function world/0 is unused  %% 这里是个警告,提醒world函数没有导出
    {ok,hello_world}
    3> m(hello_world). %% m()函数在终端显示hello_world模块信息,可以查看该模块的基本信息和导出函数列表
    Module: hello_world
    MD5: f7866776c11b9cfc904dc569bafe7995
    Compiled: No compile time info available
    Object file: /Users/snowcicada/code/erlang-story/story002/hello_world.beam
    Compiler options:  []
    Exports:
             hello/0
             module_info/0
             module_info/1
    ok
    4> hello_world:hello(). %% M:F()是Erlang的基本调用方式,M表示模块名,F表示函数名
    "Hello Erlang" %% 这里就是hello函数的返回结果
    5> hello_world:world(). %% 由于world函数没有导出,没有加入export导出列表,所以调用没导出的函数,会得到一个错误
    ** exception error: undefined function hello_world:world/0

    知识点2:编写一个有头文件的Hello World模块

      创建一个文件hello_world.hrl,就一行代码,内容如下:

    -define(TEXT, "Hello World").

      使用define声明了一个宏TEXT,这里的宏跟C语言的宏类似,语法差不多。

      修改hello_world.erl,引用下头文件,代码如下:

    -module(hello_world).-include("hello_world.hrl").
    
    %% API
    -export([hello/0, world/0]).
    
    hello() ->
      "Hello Erlang".
    
    world() ->
      ?TEXT. %% 注意这行

      Erlang要使用宏,需要在宏的前面加一个问号?,不加编译不过。

      重新编译下hello_world模块,执行结果如下:

    Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
    
    Eshell V11.1.3  (abort with ^G)
    1> ls().
    hello_world.beam     hello_world.erl      hello_world.hrl
    
    ok
    2> c(hello_world).
    {ok,hello_world}
    3> m(hello_world).
    Module: hello_world
    MD5: ceb4d19017c728b4f338ba92ea7bc0cb
    Compiled: No compile time info available
    Object file: /Users/guozs/code/erlang-story/story002/hello_world.beam
    Compiler options:  []
    Exports:
             hello/0
             module_info/0
             module_info/1
             world/0
    ok
    4> hello_world:world().
    "Hello World"

    知识点3:模块之间可以相互调用,但是不能有循环调用

      Erlang的模块可以相互调用,比如在其他语言经常会出现A包含B,B包含A的问题,但是在Erlang这里,只要避免2个模块的函数不互相循环调用,就不会有问题。什么意思呢?假设A模块有一个函数a,B模块有一个函数b,A:a调用了B:b,B:b调用了A:a,那么这样就已经循环调用了,这是不允许出现的。

      创建一个文件a.erl,代码如下:

    -module(a).
    
    %% API
    -export([a/0]).
    
    a() ->
      b:b().

      创建一个文件b.erl,代码如下:

    -module(b).%% API
    -export([b/0]).
    
    b() ->
      a:a().

      执行结果:

    Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
    
    Eshell V11.1.3  (abort with ^G)
    1> c(a).
    {ok,a}
    2> c(b).
    {ok,b}
    3> a:a(). %% 这里卡死了,只能执行Ctrl+C强制退出
    
    BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
           (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution

      程序卡死了,只能强制退出,所以模块虽然可以互相引用对方的函数,但是要注意避免循环调用问题。

    知识点4:引入模块函数

      创建一个文件calc.erl,代码如下:

    -module(calc).
    
    %% API
    -export([add/2]).
    
    add(A, B) ->
      A + B.

      修改hello_world.erl,引入calc模块的函数,代码如下:

    -module(hello_world).
    
    -include("hello_world.hrl").
    
    %% API
    -export([hello/0, world/0, mod_add/2]).
    
    -import(calc, [add/2]). %% 这里引入calc模块
    
    hello() ->
      "Hello Erlang".
    
    world() ->
      ?TEXT.
    
    mod_add(A, B) ->
      add(A, B).

      一行import只能引入一个模块,至于要引入多少函数,可以灵活选择。

      执行结果:

    Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
    
    Eshell V11.1.3  (abort with ^G)
    1> c(calc).
    {ok,calc}
    2> c(hello_world).
    {ok,hello_world}
    3> hello_world:mod %% 按Tab键可以智能提示
    mod_add/2      module_info/0  module_info/1
    3> hello_world:mod_add(1, 2).
    3

    知识点5:导出所有函数(export_all)

      首先声明,export_all要避免使用,因为会将所有的函数对外导出,会存在一些设计理念的问题。不使用export_all的好处有几个,

      1、安全性:比如当您重构模块时,您可以知道哪些功能可以安全地重命名,而不需要到外部查找依赖,万一修改了,导致其他模块调用失败也是有可能的;

      2、代码气味:编译时不会收到警告;

      3、清晰度:更容易看出在模块之外使用哪些功能。

      在函数顶部加入一行:-compile(export_all).,即可导出所有函数,但是编译时会收到一个警告。

      修改calc.erl,代码如下:

    -module(calc).
    
    %% API
    %%-export([add/2]).
    -compile(export_all).
    
    add(A, B) ->
      A + B.

      执行结果:

    Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]
    
    Eshell V11.1.3  (abort with ^G)
    1> c(calc).
    calc.erl:14: Warning: export_all flag enabled - all functions will be exported %% 这里会有警告
    {ok,calc}
    2> c(hello_world).
    {ok,hello_world}
    3> hello_world:mod_add(1,2).
    3

      模块的内容就先讲到这了,这一回只介绍模块本身,以后会经常编写代码,使用模块就是家常便饭了。

      本文使用的代码已上传Github:https://github.com/snowcicada/erlang-story/tree/main/story002

      下一回将介绍函数(Function)的使用,且听下回分解。

      

      作者:snowcicada
      本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    在Ubuntu下安装Apache
    linux 安装jdk 配置tomcat
    linux命令——rmdir
    linux命令——rm
    WebApi路由解析增加版本控制
    eclipse Dynamic web module相关问题
    mysql 5.7.18 windows zip安装
    微信扫描二维码登录网站技术原理
    Maven实战(八)——常用Maven插件介绍(下)
    Maven实战(七)——常用Maven插件介绍(上)
  • 原文地址:https://www.cnblogs.com/snowcicada/p/14166014.html
Copyright © 2011-2022 走看看