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

kel*_*ogs 0 erlang arguments list operators

** Reason for termination =
** {badarg,[{erlang,'++',[<<>>,"</after></set></query></iq>"]},
            {geoloc,get_nearby,1},
Run Code Online (Sandbox Code Playgroud)

方法是:

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.
Run Code Online (Sandbox Code Playgroud)

leg*_*cia 5

您正在尝试连接binary(<<>>)和字符串,但++只能连接两个字符串(或列表 - Erlang字符串实际上是列表).

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

一些建议:

  • 要检查列表是否为空,调用length它有点浪费,因为length需要遍历整个列表.相反,写下这样的东西:

    case After of
        "" ->
            After2 = "0";
        [_|_] ->
            After2 = After
    end
    
    Run Code Online (Sandbox Code Playgroud)

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

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

  • 你在++那里做了很多连接().在表达式中A ++ B,++运算符需要沿着整个列表行走A,因此运行时间与长度成正比A.

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

    其次,您可以使用二进制代替字符串.二进制文件在许多有趣的方面与字符串不同(至少它们在垃圾收集方面的表现如何),但它们确实具有可以有效连接的良好属性.如果是AB二进制文件,并且你编写C = <<A/binary, B/binary>>,并且编译器可以看到你只使用C但不是A之后,B将简单地连接到持有的内存区域A.有关详细信息,请参阅"效率指南"中有关二元处理的章节.

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