2016-05-07 12 views
4

Ich schreibe ein Programm, das von einem Eingang Stream liest, das heißtErlang: Lesen von einem Eingangsstrom in einer effizienten Weise

erl -run p main -noshell -s erlang halt < input 

Das Problem ist, dass es viel Zeit in Anspruch nimmt, es zu lesen (der Eingangsstrom ist sehr groß) mit dieser Lesefunktion:

read_input(L) -> 
    case io:get_line("") of 
     eof -> 
      lists:reverse(L); 
     E0 -> 
      read_input([E0|L]) 
    end. 

ich habe für effizientere Alternativen zu suchen, aber ich habe nichts gefunden. Ich habe versucht, die Datei zu lesen mit

{ok, Binary} = file:read_file("input") 

Dies ist bei weitem viel effizienter. Das Problem ist, dass ich dieses Programm auf einer Plattform ausführen muss, auf der der Name unbekannt ist, also würde ich eine Alternative dazu brauchen. zusätzlich kann ich die Flags, die beim Laufen verwendet werden, z. Flag -noinput kann nicht zur Befehlszeile hinzugefügt werden.

Welche Hilfe Sie geben können, wird begrüßt.

+0

Wenn das Lesen der gesamten Datei auf einmal akzeptabel ist, wie Sie zu zeigen scheinen, warum nicht einfach '{ok, Binary} = file: read_file (InputFile)'? Ich verstehe nicht den Punkt der Verwendung von "Katze", um die Eingabe zu kopieren und stattdessen die Kopie zu lesen. –

+0

Ist dies eine Befehlszeilenanwendung? Wird das Programm immer von der Kommandozeile aus aufgerufen? Woher kommt der Eingangsstrom? Was passiert mit den Daten nach dem Lesen? –

+0

@SteveVinoski Ich habe die Frage bearbeitet, um die Katzensache zu entfernen, weil sie verwirrend war. Ja, ich konnte es direkt aus der Datei lesen, aber ich kenne seinen Namen nicht, daher funktioniert diese Lösung nur während des Tests. –

Antwort

3

Obwohl Steve'ssolution schnellste mir bekannt Lösung kann es file Modullösung mit sehr guter Leistung verwendet werden:

-module(p). 

-export([start/0]). 

-define(BLK_SIZE, 16384). 

start() -> 
    do(), 
    halt(). 

do() -> 
    Bin = read(), 
    io:format("~p~n", [byte_size(Bin)]). 

read() -> 
    ok = io:setopts(standard_io, [binary]), 
    read(<<>>). 

read(Acc) -> 
    case file:read(standard_io, ?BLK_SIZE) of 
     {ok, Data} -> 
      read(<<Acc/bytes, Data/bytes>>); 
     eof -> 
      Acc 
    end. 

Es mit Aufruf funktioniert wie:

erl -noshell -s p < input 

Hinweis: Beide Ansätze können für die leitungsorientierte Eingabe verwendet werden, indem {line, Max_Line_Size} Option für Port oder file:read_line/1 für file Modullösung verwendet wird. Seit Version 17 (wenn ich mich richtig erinnere) gibt es einen festen Leistungsfehler in file:read_line/1 Ich fand, so ist es jetzt gut. Wie auch immer, Sie sollten Leistung und Komfort von Perl nicht erwarten.

+0

@ Hyney-Pichi-Vychdil Ihre Lösung ist genau das, was ich brauchte. Danke vielmals! Es ist wahr, dass [Steves Lösung] (http://stackoverflow.com/a/37091420/49197) effizienter ist, aber der Unterschied ist sehr gering. Ich wusste nicht, dass man auf diese Weise innerhalb eines Erlang-Programms auf die Standardeingabe verweisen könnte. Sehr nützlich und auch sehr interessant. Danke noch einmal. Ich hoffe, Ihre Antwort hilft mehr Menschen mit ähnlichen Problemen :) –

7

Sie können open_port/2 verwenden, um Stdin zu öffnen und Binärdateien daraus zu lesen. Beispiel:

-module(p). 
-export([start/0]). 

start() -> 
    process_flag(trap_exit, true), 
    P = open_port({fd,0,1}, [in, binary]), 
    Bin = read(P,<<>>), 
    io:format("received ~p\n", [Bin]), 
    halt(0). 

read(P, Bin) -> 
    receive 
     {P, {data, Data}} -> 
      read(P, <<Bin/binary, Data/binary>>); 
     {'EXIT',P,_} -> 
      Bin 
    end. 

Der Code muss Exits abfangen, damit er beim Schließen des Ports seine Leseschleife verlassen kann. In diesem Beispiel wird alles in eine einzelne Binärdatei eingelesen, die von der read/2-Funktion zurückgegeben wird. Anschließend wird sie ausgedruckt und beendet, aber Sie können natürlich weitere Operationen an der Binärdatei in Ihrer tatsächlichen Anwendung ausführen.

Sie können dies wie folgt ausführen:

erl -noinput -s p < input 
+0

Ihre Lösung ist sehr effizient, aber sie löst mein Problem nicht, weil ich das 'noinput' -Flag nicht beim Aufruf von der externen Plattform hinzufügen kann. –

+2

Ihre Frage zeigt, dass Sie '-noshell' verwenden. Warum können Sie das verwenden, aber Sie können nicht "-noinput" verwenden? Sie müssen wirklich Ihre Anforderungen in Ihrer Frage klarstellen. –

+0

Da kann ich die Flags nicht auswählen, die beim Ausführen verwendet werden. Ich brauche es, um ein Hackerrank-Problem zu lösen, bei dem die Leistung beim Lesen von Eingaben ein Muss ist. Ich klicke einfach auf eine Schaltfläche und warte auf die Ergebnisse. Die meisten von ihnen enden aufgrund einer Zeitüberschreitung, und nach dem Profiling habe ich festgestellt, dass der Hauptzeitverbraucher die Eingangslesung war. Ihre vorgeschlagene Lösung ist erstaunlich effizient, funktioniert aber leider nicht in diesem Zusammenhang. –