【问题标题】:Erlang: How to create a function that returns a string containing the date in YYMMDD format?Erlang:如何创建一个返回包含 YYMMDD 格式日期的字符串的函数?
【发布时间】:2015-11-23 18:40:46
【问题描述】:

我正在努力学习 Erlang,并且正在研究 Erlang 在网站上的实践问题。其中之一是:

编写函数 time:swedish_date(),它返回一个包含瑞典 YYMMDD 格式日期的字符串:

time:swedish_date()
"080901"

我的功能:

-module(demo).
-export([swedish_date/0]).

swedish_date() ->
[YYYY,MM,DD] = tuple_to_list(date()),
string:substr((integer_to_list(YYYY, 3,4)++pad_string(integer_to_list(MM))++pad_string(integer_to_list(DD)).

pad_string(String) ->
if 
    length(String) == 1 -> '0' ++ String;
    true -> String
end.

编译时出现以下错误。

demo.erl:6: syntax error before: '.'
demo.erl:2: function swedish_date/0 undefined
demo.erl:9: Warning: function pad_string/1 is unused 
error

我该如何解决这个问题?

【问题讨论】:

    标签: functional-programming erlang erlang-shell


    【解决方案1】:

    修复编译错误后,您仍然面临运行时错误。由于您正在尝试学习 Erlang,因此查看您的方法并查看它是否可以改进并在此过程中修复这些运行时错误是很有启发性的。

    首先我们来看swedish_date/0

    swedish_date() ->
        [YYYY,MM,DD] = tuple_to_list(date()),
    

    为什么要将列表转换为元组?由于您单独使用列表元素并且从不使用整个列表,因此转换没有任何意义。您可以改为模式匹配返回的元组:

        {YYYY,MM,DD} = date(),
    

    接下来,您正在调用 string:substr/1,它不存在:

        string:substr((integer_to_list(YYYY,3,4) ++
            pad_string(integer_to_list(MM)) ++
            pad_string(integer_to_list(DD))).
    

    string:substr/2,3 functions 都采用起始位置,3 元版本也采用长度。你也不需要,并且可以完全避免string:substr,而只是返回组装的字符串:

        integer_to_list(YYYY,3,4) ++
            pad_string(integer_to_list(MM)) ++
            pad_string(integer_to_list(DD)).
    

    哎呀,这还是不对:没有integer_to_list/3这样的功能,所以只需将第一个调用替换为integer_to_list/1

        integer_to_list(YYYY) ++
            pad_string(integer_to_list(MM)) ++
            pad_string(integer_to_list(DD)).
    

    接下来我们看pad_string/1

    pad_string(String) ->
        if
            length(String) == 1 -> '0' ++ String;
            true -> String
        end.
    

    这里有一个运行时错误,因为'0' 是一个原子,而您正试图将String(它是一个列表)附加到它。错误如下所示:

    ** 异常错误:参数错误
    in 运算符 ++/2
    称为 '0' ++ "8"

    让我们考虑一下pad_string/1 的作用,而不是直接修复它:如果字符串是单个数字,它会添加一个前导0 字符。不要使用if 来检查这种情况——if 在 Erlang 代码中并不经常使用——而是使用模式匹配:

    pad_string([D]) ->
        [$0,D];
    pad_string(S) ->
        S.
    

    第一个子句匹配一个单元素列表,并返回一个新列表,其中元素D 前面有$0,这是字符0 的字符常量。第二个子句匹配所有其他参数,只返回传入的任何内容。

    这是包含所有更改的完整版本:

    -module(demo).
    -export([swedish_date/0]).
    
    swedish_date() ->
        {YYYY,MM,DD} = date(),
        integer_to_list(YYYY) ++
            pad_string(integer_to_list(MM)) ++
            pad_string(integer_to_list(DD)).
    
    pad_string([D]) ->
        [$0,D];
    pad_string(S) ->
        S.
    

    但更简单的方法是使用the io_lib:format/2 function 直接格式化所需的字符串:

    swedish_date() ->
        io_lib:format("~w~2..0w~2..0w", tuple_to_list(date())).
    

    首先,请注意,我们将重新调用tuple_to_list(date())。这是因为io_lib:format/2 的第二个参数必须是一个列表。它的第一个参数是一个格式字符串,在我们的例子中表示期望三个参数,将每个参数格式化为一个 Erlang 术语,并将第二个和第三个参数格式化为宽度为 2 和 0 填充。

    但还有一步需要解决,因为如果我们运行io_lib:format/2 版本,我们会得到:

    1> demo:swedish_date().
    ["2015",["0",56],"29"]
    

    哇,那是什么?它只是一个深度列表,其中列表的每个元素本身就是一个列表。为了得到我们想要的格式,我们可以展平这个列表:

    swedish_date() ->
        lists:flatten(io_lib:format("~w~2..0w~2..0w", tuple_to_list(date()))).
    

    执行这个版本给了我们想要的东西:

    2> demo:swedish_date().
    "20150829"
    

    在下面找到代码的最终完整版本。

    -module(demo).
    -export([swedish_date/0]).
    
    swedish_date() ->
        lists:flatten(io_lib:format("~w~2..0w~2..0w", tuple_to_list(date()))).
    

    更新:@Pascal cmets 应该将年份打印为 2 位而不是 4。我们可以通过列表解析传递日期列表来实现这一点:

    swedish_date() ->
        DateVals = [D rem 100 || D <- tuple_to_list(date())],
        lists:flatten(io_lib:format("~w~2..0w~2..0w", DateVals)).
    

    这会将rem 余数运算符应用于tuple_to_list(date()) 返回的每个列表元素。每月和每天都不需要该操作,但我认为它比提取年份并单独处理要干净。结果:

    3> demo:swedish_date().
    "150829"
    

    【讨论】:

    • 它打印的是“20150829”,而不是“150829”,这就是我使用 YYYY rem 100 的原因。
    【解决方案2】:

    除了第 6 行的括号错误之外,您在第 10 行还有一个错误,您使用了“0”而不是“0”的形式,因此您定义的是原子而不是字符串。

    我知道您这样做是出于教育目的,但我鼓励您深入研究 erlang 库,这是您必须要做的事情。对于这样的常见问题,它已经存在可以帮助您的功能:

    swedish_date() ->
        {YYYY,MM,DD} = date(), % not useful to transform into list
        lists:flatten(io_lib:format("~2.10.0B~2.10.0B~2.10.0B",[YYYY rem 100,MM,DD])).
        % ~X.Y.ZB means: uses format integer in base Y, print X characters, uses Z for padding
    

    【讨论】:

      【解决方案3】:

      这里有几个问题:

      1. 第 6 行末尾缺少括号。
      2. 当 Erlang 仅定义 integer_to_list/1,2 时,您正在尝试调用 integer_to_list/3

      这将起作用:

      -module(demo).
      -export([swedish_date/0]).
      
      swedish_date() ->
      [YYYY,MM,DD] = tuple_to_list(date()),
      string:substr(
          integer_to_list(YYYY) ++ 
          pad_string(integer_to_list(MM)) ++ 
          pad_string(integer_to_list(DD))
          ).
      
      pad_string(String) ->
      if 
          length(String) == 1 -> '0' ++ String;
          true -> String
      end.
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-11
        • 2015-10-02
        • 2022-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多