2009-01-23 12 views
6

Dies ist eine Erlang Frage.Unerwartetes Verhalten von io: fread in Erlang

Ich bin in ein unerwartetes Verhalten von io: fread geraten.

Ich frage mich, ob jemand überprüfen könnte, ob etwas mit der Art und Weise, wie ich io: fread benutze, nicht stimmt oder ob es einen Fehler in io: fread gibt.

Ich habe eine Textdatei, die ein „Dreieck von Zahlen“ enthält, wie folgt:

 
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 
... 

Es gibt einen einzigen Raum zwischen jedem Paar von Zahlen und jede Zeile endet mit einem Wagenrücklauf new-Leitungspaar .

Ich benutze das folgende Erlang-Programm, um diese Datei in eine Liste zu lesen.

 
-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. 

Der Ausgang dieses Programms ist:

 
([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|...] 

Hinweis, wie die letzte Zahl der vierten Leitung (34) und der ersten Reihe der fünften Leitung (10) zu einem einzigen verschmolzen Nummer 3410.

Wenn ich die Textdatei mit "od" ablege, gibt es nichts besonderes an diesen Zeilen; sie enden mit cr-nl wie jede andere Linie:

 
> 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 

Eine interessante Beobachtung ist, dass einige der Zahlen, für das das Problem auf 16-Byte-Grenze in der Textdatei (aber nicht alle sein passieren auftritt, zum Beispiel 6890).

+0

Dies sieht für alles wie ein Fehler aus. Wenn Sie die Länge der Daten angeben, sind es 32 Elemente, nicht 36 Elemente. Die Anordnung der Daten in anderen Formaten verschiebt den Fehler einfach. (Ich habe es in Vista + Erland 5.6.5 versucht). – Godeke

Antwort

9

Ich werde damit gehen, dass es auch in Erlang ein Bug ist, und ein komischer. Ändern der Formatstring „~ 2s“ gibt ebenso seltsame Ergebnisse:

["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", 
[...]|...] 

So scheint es, dass es ein Newline-Zeichen als reguläres Zeichen für die Zwecke der Zählung zählt schon, aber nicht, wenn es um die Herstellung des Ausgangs kommt. Loopy wie die Hölle.

Eine Woche Erlang Programmierung, und ich bin bereits in die Quelle eintauchen. Das könnte ein neuer Rekord für mich ...

EDIT

Ein bisschen mehr Untersuchung sein hat mir bestätigt, dass dies ein Fehler ist. Der Aufruf einen der internen Methoden, die in fread verwendet wird:

> 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"} 

Grundsätzlich, wenn es mehrere Werte ist zu lesen, dann eine neue Zeile, wird die erste Newline im gegessen „noch gelesen werden“ Teil der Zeichenfolge. Andere Tests legen nahe, dass es OK ist, wenn Sie ein Leerzeichen voranstellen, und wenn Sie den String mit einem Zeilenumbruch führen, werden Sie nach mehr gefragt.

Ich werde dem auf den Grund gehen, gosh-darn-it ... (grins) Es gibt nicht so viel Code zum Durchlaufen, und nicht viel davon befasst sich speziell mit Zeilenumbrüchen, also sollte es nicht ' Es dauert zu lange, um es einzugrenzen und zu beheben.

EDIT^2

HA HA! Hab das kleine Feuerzeug bekommen.

Hier ist der Patch auf die stdlib, die Sie wollen (Sie erinnern sich die neue Strahl Datei über den oberen Teil der alten neu kompilieren und Drop):

--- ../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} 

Nun meine Patch einreichen, erlang-Patches und Ernten Sie den daraus resultierenden Ruhm und Ruhm ...

+0

Sieht aus, als ob Sie wärmer werden. Danke, dass Sie sich die Zeit genommen haben, diesem Thema auf den Grund zu gehen. –

0

Ich bemerkte, dass es mehrere Instanzen gibt, in denen zwei Zahlen zusammengeführt werden, und es scheint, dass es an den Liniengrenzen in jeder Zeile beginnend mit der vierten Zeile und darüber hinaus ist.

Ich fand, dass, wenn Sie ein Leerzeichen zu Beginn jeder Zeile an der fünften Start hinzufügen, das heißt:

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 
... 

Die Zahlen gehen richtig analysiert:

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|...] 

Es funktioniert auch wenn Sie das Leerzeichen auch an den Anfang der ersten vier Zeilen hinzufügen.

Es ist eher eine Problemumgehung als eine tatsächliche Lösung, aber es funktioniert. Ich würde gerne herausfinden, wie man die Formatzeichenfolge für io: fread so einrichtet, dass wir das nicht tun müssten.

UPDATE Hier ist eine Problemumgehung, die Sie nicht zwingt, die Datei zu ändern. Dies setzt voraus, dass alle Ziffern sind zwei Zeichen (< 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. 

Grundsätzlich fängt der Code eine der Nummern, welche die Verkettung von zwei über einen Neue-Zeile sind, und teilt sie in zwei.

Wieder ist es ein Klud, der einen möglichen Fehler in io: fread impliziert, aber das sollte es tun.

UPDATE erneut die oben nur für zweistellige Eingaben funktionieren wird, aber da das Beispiel packt alle Ziffern (auch die < 10) in einem zweistelligen Format, die für dieses Beispiel funktioniert.

+0

Danke! Das hilft.Wenn Sie eine Format-Zeichenfolge finden, die funktioniert, wäre das sicherlich auch hilfreich. Aber um sicherzugehen, dass wir auf der gleichen Seite sind: Denken Sie, dass die Formatzeichenfolge, die ich jetzt verwende (nämlich "~ d"), mit meiner ursprünglichen Datei funktionieren sollte? Mit anderen Worten: Es gibt einen Fehler in io: fread? –

+0

Ich sehe keinen Grund, warum es nicht mit der Originaldatei funktionieren sollte, aber ich bin immer noch etwas Neues für Erlang, also könnte mir etwas fehlen. Es könnte sicherlich ein Fehler sein, aber ich bin mir nicht sicher, an diesem Punkt. – Vector

+0

Ihre Problemumgehung geht auch davon aus, dass alle Zahlen genau zwei Ziffern sind - es funktioniert nicht für 1 \ n2, da es 12 ergibt und nicht geteilt wird. – womble

1

Neben der Tatsache, dass es scheint, um ein Fehler in einer der Erlang-Bibliotheken zu sein Ich denke, Sie könnten (sehr) leicht das Problem umgehen.

Angesichts der Tatsache, dass Ihre Datei linienorientiert ist, denke ich, dass Sie sie auch Zeile für Zeile verarbeiten.

Betrachten Sie die folgende Konstruktion. Es funktioniert gut auf einem ungepatchten Erlang, und weil es eine faule Auswertung verwendet, kann es mit Dateien beliebiger Länge umgehen, ohne es erst vollständig in den Speicher einzulesen. Das Modul enthält ein Beispiel für eine Funktion, die auf jede Zeile angewendet werden kann - indem Sie eine Textzeile von Ganzzahlen in eine Liste von Ganzzahlen umwandeln.


-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 

Prost, h.

Verwandte Themen