Shell 在 Erlang 进程中多次打印输入

Shell within Erlang process prints input many times

我用 Erlang 为一种语言构建了一个 REPL。我运行它来自shell,像这样:

repl:main(Args).

它使用 io_getchars 读取,直到遇到 ;,然后对其进行解析和词法分析:

getchar(eof) -> throw(eof);
getchar([C]) -> C.
getchar() -> getchar(io:get_chars("", 1)).

read_term() ->
    case getchar() of
        $; -> ";";
        C -> [C|read_term()]
    end.

一切都很好,但是与 repl 的交互看起来像这样:

willow% erl -name MyName 
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Eshell V7.3  (abort with ^G)
(MyName)1> repl:main(Args).
Master is <0.39.0> in init_workers
1> map(fn(x)=x+1, '[1 2 3 4 5]);      
ap(fn(x)=x+1, '[1 2 3 4 5]);
p(fn(x)=x+1, '[1 2 3 4 5]);
(fn(x)=x+1, '[1 2 3 4 5]);
fn(x)=x+1, '[1 2 3 4 5]);
n(x)=x+1, '[1 2 3 4 5]);
(x)=x+1, '[1 2 3 4 5]);
x)=x+1, '[1 2 3 4 5]);
)=x+1, '[1 2 3 4 5]);
=x+1, '[1 2 3 4 5]);
x+1, '[1 2 3 4 5]);
+1, '[1 2 3 4 5]);
1, '[1 2 3 4 5]);
, '[1 2 3 4 5]);
 '[1 2 3 4 5]);
'[1 2 3 4 5]);
[1 2 3 4 5]);
1 2 3 4 5]);
 2 3 4 5]);
2 3 4 5]);
 3 4 5]);
3 4 5]);
 4 5]);
4 5]);
 5]);
5]);
]);
);
;
Delegated packet 0 
Delegated packet 1 
Delegated packet 2 
Delegated packet 3 
Delegated packet 4 
(2 3 4 5 6)
2> 

我也试过:

read_term(Acc) ->
    case io:request(standard_io, {get_until, '', scanner, token, [1]}) of
        {ok, EndToken={';;', _}, _} -> Acc ++ [EndToken];
        {ok, Token, _} -> read_term(Acc ++ [Token]);
        {error, token} -> {error, scanning_error};
        {eof, _} -> Acc
    end.

不幸的是,它具有相同的效果。

我不想看到我输入的字符串的所有可能的右移片段。是什么原因造成的,我该如何阻止它?

编辑:如果我从 shell 中 运行 改为 erl -noshell -eval 'repl:main()',则不会发生此打印。为什么?

您将不得不改用 io:get_line,并遍历接收到的字符串。然后你必须决定如果接收到的字符串不包含任何“;”

例如:

1> Eval = fun(X) -> io:format("evaluation of ~p~n",[X]) end.                                            
#Fun<erl_eval.6.50752066>
2> F = fun() -> 
       R = io:get_line("Enter a string : "),
       L = string:tokens(R,";"),
       [Eval(X) || X <- L]
   end.
#Fun<erl_eval.20.50752066>
3> F().                                                                                                 
Enter a string :  map(fn(x)=x+1, '[1 2 3 4 5]);map(fn(x)=x+5, '[1 2 3 4 5]);uncomplete                                
evaluation of " map(fn(x)=x+1, '[1 2 3 4 5])"
evaluation of "map(fn(x)=x+5, '[1 2 3 4 5])"
evaluation of "uncomplete\n"
[ok,ok,ok]
4> F().                                                                 
Enter a string :  map(fn(x)=x+1, '[1 2 3 4 5]);                                       
evaluation of " map(fn(x)=x+1, '[1 2 3 4 5])"
evaluation of "\n"
[ok,ok]       
5>

[编辑]

io:get_chars/2 正在从 shell 输入中获取作为参数给出的字符数,在您的情况下为 1,其余的让其使用。 shell 不知道您将再次调用 io:get_chars/2 函数,因此它再次打印输入字符串的其余部分,并且在每次调用 [=16 时一次又一次地执行相同的操作=] 函数,这就是为什么你会看到这种奇怪的行为。

io:get_line/2 从 shell 和 return 中获取整个输入字符串(在 erlang 中它是一个整数列表),这就是为什么你有一个干净的行为在shellwindow中,那么就由你来选择你喜欢的算法来分析这个字符串了。我选择 string:tokens/2 是因为我尝试在它们存在时使用库函数,我认为它更容易阅读,并且不需要文档 :o)

如果您不想处理可能的错误输入,可以将 F/0 函数替换为

2> F = fun() ->                               
2>     Rep = io:get_line("Enter a string : "),
2>     [L,"\n"] = string:tokens(Rep,";"),     
2>     L
2> end.

它会和你的 read_term/0 函数做同样的事情(除了它删除最后一个 $;,但它会检查它是否存在):

3> F().                                  
Enter a string : correct input;
"correct input"
4> F().          
Enter a string : missing semi colon
** exception error: no match of right hand side value ["missing semi colon\n"]
5> F().              
Enter a string : correct input; but extracharacters after semi colon
** exception error: no match of right hand side value ["correct input"," but extracharacters after semi colon\n"]