badarg to'++'operator - 怎么来的?

时间:2013-08-26 01:10:05

标签: list erlang arguments operators

** Reason for termination =
** {badarg,[{erlang,'++',[<<>>,"</after></set></query></iq>"]},
            {geoloc,get_nearby,1},

方法是:

get_nearby({_Pid, DynVars})->
        %Last = ts_dynvars:lookup(last, DynVars),
        Last = lists:keysearch(last,1,DynVars),
        {ok, Rad} = ts_dynvars:lookup(rad,DynVars),
        {ok, Lat} = ts_dynvars:lookup(lat,DynVars),
        {ok, Lon} = ts_dynvars:lookup(lon,DynVars),
        if is_tuple(Last) ->
                {value,{Key,After}} = Last,
                if length(After) == 0 ->
                        After2 = "0";
                true ->
                        After2 = After
                end,
                "<iq id=\"" ++ common:get_random_string(5,"abcdefghijklmnopqrstuvwxyz0123456789-+=") ++ "\" xmlns=\"http://xmpp.xgate.com.hk/plugins\" to=\"xmpp.xgate.hk.com\" type=\"get\"><query xmlns=\"jabber:iq:geoloc\"><geoloc><lat>" ++ Lat ++ "</lat><lon>" ++ Lon ++ "</lon><radius>" ++ Rad ++ "</radius></geoloc><set xmlns=\"http://jabber.org/protocol/rsm\"><max>" ++ integer_to_list(ran_max()) ++ "</max><after>" ++ After2 ++ "</after></set></query></iq>";
        true ->         % Last is boolean, namely the 'false' atom
                ts_dynvars:set([rad, lat, lon], [Rad, Lat, Lon], DynVars),
                "<iq id=\"" ++ common:get_random_string(5,"abcdefghijklmnopqrstuvwxyz0123456789-+=") ++ "\" xmlns=\"http://xmpp.xgate.com.hk/plugins\" to=\"xmpp.xgate.hk.com\" type=\"get\"><query xmlns=\"jabber:iq:geoloc\"><geoloc><lat>" ++ Lat ++ "</lat><lon>" ++ Lon ++ "</lon><radius>" ++ Rad ++ "</radius></geoloc><set xmlns=\"http://jabber.org/protocol/rsm\"><max>" ++ integer_to_list(ran_max()) ++ "</max></set></query></iq>"
        end.

1 个答案:

答案 0 :(得分:5)

您正在尝试连接二进制文件(<<>>)和字符串,但++只能连接两个字符串(或列表 - Erlang字符串实际上是列表)。

这意味着After2是二进制文件,因此它在if表达式的第二个子句中接收到此值。通常在length(After)不是列表时调用After会导致badarg异常,但是当它出现在if测试中时,它会被视为保护测试,并忽略异常,因此length(After) == 0被视为假。因此,当您在DynVars中获得相应的值时,相应的值已经是二进制值。

一些建议:

  • 要检查列表是否为空,在其上调用length有点浪费,因为length需要遍历整个列表。相反,写下类似的东西:

    case After of
        "" ->
            After2 = "0";
        [_|_] ->
            After2 = After
    end
    

    [_|_]是一种匹配非空列表的模式。在您的情况下,After的值与任何子句都不匹配,并且您有一个case_clause错误,告诉您实际得到的值。

    当然,如果你真的希望在这里使用二进制文件,请检查<<>><<_/binary>>

  • 你在那里进行了很多连接(++)。在表达式A ++ B中,++运算符需要遍历A中的整个列表,因此运行时间与A的长度成正比。

    连接有两种常见的替代方法。首先,通常会消耗结果的函数实际上不需要平面列表,但对“深度列表”或“iolist”同样满意 - 而不是"foo" ++ "bar",写["foo", "bar"]。值得注意的是,如果您要将结果写入文件或将其发送到套接字,则file:writegen_tcp:send都接受这两种变体。

    其次,您可以使用二进制文件而不是字符串。二进制文件在许多有趣的方面与字符串不同(至少它们在垃圾收集方面的表现如何),但它们确实具有可以有效连接的良好属性。如果AB是二进制文件,并且您编写C = <<A/binary, B/binary>>,并且编译器可以看到您之后仅使用C而不是A,{{{ 1}}将简单地连接到保留B的内存区域。有关详细信息,请参阅the chapter on binary handling in the Efficiency Guide

  • A开头的两行几乎完全相同,只是第一行在中间插入"<iq id=\""。您可以将第一个case子句设置为"<after>" ++ After2 ++ "</after>",将第二个case子句设置为MaybeAfter = "<after>" ++ After2 ++ "</after>",然后使用一行在正确的位置插入MaybeAfter = ""的值。这将有助于使代码更具可读性。