erlang

Comportamiento inesperado de io: fread en Erlang



!= erlang (3)

Iba a ser un error en Erlang, y uno extraño. Cambiar la cadena de formato a "~ 2s" da resultados igualmente extraños:

["59","73","4","15","2","40","0","92","6","53","0","6","34", "10","5","1","87","8","6","81","61","9","5","66","5","7", "25","6", [...]|...]

Parece que se está contando un personaje de nueva línea como un personaje regular para el conteo, pero no cuando se trata de producir el resultado. Loopy como todo el infierno.

Una semana de programación de Erlang, y ya estoy profundizando en la fuente. Eso podría ser un nuevo récord para mí ...

EDITAR

Un poco más de investigación me ha confirmado que esto es un error. Llamar a uno de los métodos internos que se usan en fread :

> io_lib_fread:fread([], "12 13/n14 15 16/n17 18 19 20/n", "~d"). {done,{ok,"/f"}," 1314 15 16/n17 18 19 20/n"}

Básicamente, si hay varios valores para leer, entonces una nueva línea, la primera nueva línea se come en la parte "todavía por leer" de la cadena. Otras pruebas sugieren que si antepones un espacio, está bien, y si lideras la cadena con una nueva línea, pide más.

Voy a llegar al fondo de esto, Dios mío ... (sonrisa) No hay mucho código por recorrer, y no mucho trata específicamente con líneas nuevas, por lo que no debería tomar demasiado tiempo para estrecharlo y arreglarlo.

EDITAR ^ 2

¡JAJA! Tengo el pequeño blighter.

Aquí está el parche para el stdlib que desee (recuerde volver a compilar y colocar el nuevo archivo de rayo sobre el anterior):

--- ../erlang/erlang-12.b.3-dfsg/lib/stdlib/src/io_lib_fread.erl +++ ./io_lib_fread.erl @@ -35,9 +35,9 @@ fread_collect(MoreChars, [], Rest, RestFormat, N, Inputs). fread_collect([$/r|More], Stack, Rest, RestFormat, N, Inputs) -> - fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, More); + fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, [$/r|More]); fread_collect([$/n|More], Stack, Rest, RestFormat, N, Inputs) -> - fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, More); + fread(RestFormat, Rest ++ reverse(Stack), N, Inputs, [$/n|More]); fread_collect([C|More], Stack, Rest, RestFormat, N, Inputs) -> fread_collect(More, [C|Stack], Rest, RestFormat, N, Inputs); fread_collect([], Stack, Rest, RestFormat, N, Inputs) -> @@ -55,8 +55,8 @@ eof -> fread(RestFormat,eof,N,Inputs,eof); _ -> - %% Don''t forget to count the newline. - {more,{More,RestFormat,N+1,Inputs}} + %% Don''t forget to strip and count the newline. + {more,{tl(More),RestFormat,N+1,Inputs}} end; Other -> %An error has occurred {done,Other,More}

Ahora, para enviar mi parche a erlang-patches, y cosechar la fama y gloria resultante ...

Esta es una pregunta de Erlang.

Me he encontrado con un comportamiento inesperado por io: fread.

Me preguntaba si alguien podría verificar si hay algún problema con la forma en que uso io: fread o si hay un error en io: fread.

Tengo un archivo de texto que contiene un "triángulo de números" de la siguiente manera:

59 73 41 52 40 09 26 53 06 34 10 51 87 86 81 61 95 66 57 25 68 90 81 80 38 92 67 73 30 28 51 76 81 18 75 44 ...

Hay un espacio único entre cada par de números y cada línea termina con un par de líneas nuevas de retorno de carro.

Uso el siguiente programa Erlang para leer este archivo en una lista.

-module(euler67). -author(''Cayle Spandon''). -export([solve/0]). solve() -> {ok, File} = file:open("triangle.txt", [read]), Data = read_file(File), ok = file:close(File), Data. read_file(File) -> read_file(File, []). read_file(File, Data) -> case io:fread(File, "", "~d") of {ok, [N]} -> read_file(File, [N | Data]); eof -> lists:reverse(Data) end.

El resultado de este programa es:

([email protected])30> euler67:solve(). [59,73,41,52,40,9,26,53,6,3410,51,87,86,8161,95,66,57,25, 6890,81,80,38,92,67,7330,28,51,76,81|...]

Observe cómo el último número de la cuarta línea (34) y el primer número de la quinta línea (10) se han fusionado en un solo número 3410.

Cuando descargo el archivo de texto usando "od" no hay nada de especial en esas líneas; terminan con cr-nl como cualquier otra línea:

> od -t a triangle.txt 0000000 5 9 cr nl 7 3 sp 4 1 cr nl 5 2 sp 4 0 0000020 sp 0 9 cr nl 2 6 sp 5 3 sp 0 6 sp 3 4 0000040 cr nl 1 0 sp 5 1 sp 8 7 sp 8 6 sp 8 1 0000060 cr nl 6 1 sp 9 5 sp 6 6 sp 5 7 sp 2 5 0000100 sp 6 8 cr nl 9 0 sp 8 1 sp 8 0 sp 3 8 0000120 sp 9 2 sp 6 7 sp 7 3 cr nl 3 0 sp 2 8 0000140 sp 5 1 sp 7 6 sp 8 1 sp 1 8 sp 7 5 sp 0000160 4 4 cr nl 8 4 sp 1 4 sp 9 5 sp 8 7 sp

Una observación interesante es que algunos de los números para los que ocurre el problema se encuentran en un límite de 16 bytes en el archivo de texto (pero no todos, por ejemplo, 6890).


Noté que hay varias instancias en las que se combinan dos números, y parece estar en los límites de la línea en cada línea que comienza en la cuarta línea y más allá.

Descubrí que si agrega un carácter de espacio en blanco al comienzo de cada línea que comienza en el quinto, es decir:

59 73 41 52 40 09 26 53 06 34 10 51 87 86 81 61 95 66 57 25 68 90 81 80 38 92 67 73 30 28 51 76 81 18 75 44 ...

Los números se analizan correctamente:

39> euler67:solve(). [59,73,41,52,40,9,26,53,6,34,10,51,87,86,81,61,95,66,57,25, 68,90,81,80,38,92,67,73,30|...]

También funciona si agrega los espacios en blanco al comienzo de las primeras cuatro líneas también.

Es más una solución que una solución real, pero funciona. Me gustaría descubrir cómo configurar la cadena de formato para io: fread de modo que no tengamos que hacer esto.

ACTUALIZAR Aquí hay una solución que no lo forzará a cambiar el archivo. Esto supone que todos los dígitos son dos caracteres (<100):

read_file(File, Data) -> case io:fread(File, "", "~d") of {ok, [N] } -> if N > 100 -> First = N div 100, Second = N - (First * 100), read_file(File, [First , Second | Data]); true -> read_file(File, [N | Data]) end; eof -> lists:reverse(Data) end.

Básicamente, el código captura cualquiera de los números que son la concatenación de dos a través de una línea nueva y los divide en dos.

Nuevamente, es un error que implica un posible error en io: fread, pero eso debería hacerlo.

ACTUALIZAR DE NUEVO Lo anterior solo funcionará para entradas de dos dígitos, pero dado que el ejemplo incluye todos los dígitos (incluso aquellos <10) en un formato de dos dígitos, eso funcionará para este ejemplo.


Además del hecho de que parece ser un error en una de las libs de erlang, creo que podrías eludir (muy) fácilmente el problema.

Dado que su archivo está orientado a líneas, creo que la mejor práctica es que también lo procesa línea por línea.

Considera la siguiente construcción. Funciona muy bien en un erlang no parcheado y debido a que utiliza evaluación diferida, puede manejar archivos de longitud arbitraria sin tener que leer todo en la memoria primero. El módulo contiene un ejemplo de una función para aplicar a cada línea, convirtiendo una línea de representaciones de texto de enteros en una lista de enteros.

-module(liner). -author("Harro Verkouter"). -export([liner/2, integerize/0, lazyfile/1]). % Applies a function to all lines of the file % before reducing (foldl). liner(File, Fun) -> lists:foldl(fun(X, Acc) -> Acc++Fun(X) end, [], lazyfile(File)). % Reads the lines of a file in a lazy fashion lazyfile(File) -> {ok, Fd} = file:open(File, [read]), lazylines(Fd). % Actually, this one does the lazy read ;) lazylines(Fd) -> case io:get_line(Fd, "") of eof -> file:close(Fd), []; {error, Reason} -> file:close(Fd), exit(Reason); L -> [L|lazylines(Fd)] end. % Take a line of space separated integers (string) and transform % them into a list of integers integerize() -> fun(X) -> lists:map(fun(Y) -> list_to_integer(Y) end, string:tokens(X, " /n")) end. Example usage: Eshell V5.6.5 (abort with ^G) 1> c(liner). {ok,liner} 2> liner:liner("triangle.txt", liner:integerize()). [59,73,41,52,40,9,26,53,6,34,10,51,87,86,81,61,95,66,57,25, 68,90,81,80,38,92,67,73,30|...] And as a bonus, you can easily fold over the lines of any (lineoriented) file w/o running out of memory :) 6> lists:foldl( fun(X, Acc) -> 6> io:format("~.2w: ~s", [Acc,X]), Acc+1 6> end, 6> 1, 6> liner:lazyfile("triangle.txt")). 1: 59 2: 73 41 3: 52 40 09 4: 26 53 06 34 5: 10 51 87 86 81 6: 61 95 66 57 25 68 7: 90 81 80 38 92 67 73 8: 30 28 51 76 81 18 75 44

Saludos, h.