Zuerst habe ich die objektorientierte Programmierung und ihre Funktionen zusammengefasst, aber ich konnte es nicht gut machen und gab auf.
Stattdessen dachte ich, es wäre besser, eine einfache Aufgabe zu erstellen, sie konkret zu programmieren und zu vergleichen, und entschied mich daher, mit einem einfachen Additionsprogramm zu beginnen.
Als Sprache habe ich Pascal (Standard-Pascal) als prozedurorientierte Sprache, Java als objektorientierte Sprache und Haskell als funktionale Sprache gewählt.
Diese Sprachwahl ist ein Hobby. Ich kann es nicht gut sagen, aber ich mag eine Sprache, die ein einziges Paradigma und statische Typen hat, die eine lange Syntax hat und die sich ein bisschen "matschig" anfühlt.
Die Addition berechnet nicht nur in einer Zeile, sondern akzeptiert Eingaben des Benutzers und definiert die Funktion wie folgt.
1.Zunächst wird die Meldung "Geben Sie eine Nummer ein. Sie endet mit einer Leerzeile." Angezeigt.
2. 「>Wird angezeigt und wartet auf Eingabe. Wenn Sie eine Zahl eingeben und eine Zeile unterbrechen, wird diese aufgezeichnet
3.nochmal">Wird angezeigt und wartet auf Eingabe. Wiederholen Sie diesen Vorgang bis zu einem Zeilenumbruch, ohne Zeichen einzugeben.
4.Wenn ein Zeilenumbruch auftritt, wird das Ergebnis der Addition der Summe aller bisher eingegebenen Zahlen angezeigt. "Die Summe ist xxx" wird angezeigt.
5.Ignorieren Sie Eingaben, die andere Zeichen als Zahlen enthalten.
Eigentlich denke ich, dass ein etwas komplizierteres Beispiel die Individualität jeder Sprache hervorheben wird, aber der erste Schritt ist das.
Das tatsächliche Betriebsbild ist wie folgt.
Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.
>10
>20
>4
>
Die Summe beträgt 34.
Dieser Artikel richtet sich an Anfänger, die eine Programmiersprache gelernt haben und die bedingte Verzweigung und Schleifenverarbeitung verstehen.
Betrachten wir hier Standard-Pascal. Standard Pascal hat nur wenige Funktionen und kann nicht aufgeteilt werden, aber ich denke, es ist immer noch zum Erlernen der Programmierung geeignet, insbesondere für die Grundlagen von Lernalgorithmen.
Betrachten Sie zunächst die Implementierung der Hauptroutine wie folgt:
program CalcSumApp;
type
ItemPtr = ^Item;
Item =
record
next: ItemPtr;
text: ShortString;
end;
//Definition verschiedener Funktionen
var
lines : ItemPtr;
sum : Integer;
begin
writeln('Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.');
lines := readLines();
sum := sumOfLines(lines);
writeln('Die Summe ist' + toStr(sum) + 'ist.');
disposeAll(lines);
end.
Zuerst definieren wir einen Zeiger namens ItemPtr. Tatsächlich kann in Standard-Pascal (ISO 7185 Pascal) kein Array mit variabler Länge verwendet werden. Daher definieren wir hier einen Knoten, der nur einen numerischen Wert namens Item speichern und in Form eines Zeigers eingeben kann. Geändert, um den numerischen Wert zu speichern.
Die Hauptroutine (der Teil unter begin
) sieht folgendermaßen aus:
Die Nachricht wird in der ersten Zeile angezeigt und der numerische Wert (mehrere Zeilen) wird in der zweiten Zeile akzeptiert.
In der 3. Zeile wird dann die Summe der eingegebenen Zahlenwerte berechnet und in der 4. Zeile das Ergebnis angezeigt. toStr
ist eine Funktion, die eine Ganzzahl in eine Zeichenfolge konvertiert. Dies ist jedoch auch nicht im Standard-Pascal enthalten und wird daher im Programm implementiert.
Die fünfte Zeile gibt Speicher frei. Sie müssen den Zeiger selbst loslassen, wenn Sie fertig sind.
Betrachten wir nun die Implementierung von "readLines" im Eingabeteil.
function readLines(): ItemPtr;
var
result: ItemPtr;
prevItemPtr: ItemPtr;
currItemPtr: ItemPtr;
line: ShortString;
begin
result := nil;
prevItemPtr := nil;
while true do
begin
write('>');
readln(line);
if line <> '' then
begin
if prevItemPtr = nil then
begin
new(prevItemPtr);
prevItemPtr^.text := line;
prevItemPtr^.next := nil;
result := prevItemPtr;
end
else
begin
new(currItemPtr);
currItemPtr^.text := line;
currItemPtr^.next := nil;
prevItemPtr^.next := currItemPtr;
prevItemPtr := currItemPtr;
end;
end
else
break;
end;
readLines := result;
end;
Schließen Sie das Ganze in eine while-Schleife ein und machen Sie es zu einem Format, das wiederholt wird, bis eine Bedingung (Eingabe eines leeren Zeilenvorschubs) erfüllt ist.
readln (line)
ist der Teil, der die Zeicheneingabe empfängt.
Nur das erste Mal ist etwas Besonderes und behält den eingegebenen Wert in prevItemPtr
bei. new (prevItemPtr)
ist eine Anweisung, einen Bereich im Heapspeicher zuzuweisen, den eingegebenen numerischen Wert dort zu speichern und im Ergebnis zu speichern (result
).
Ab dem zweiten Mal bleibt der eingegebene Wert in "currItemPtr" erhalten. Dann wird der eingegebene numerische Wert gespeichert und aus dem vorherigen "prevItemPtr" referenziert. Zum Schluss aktualisieren Sie currItemPtr
als neues prevItemPtr
.
Als nächstes folgt die Funktion sumOfLines
, die die Addition berechnet.
function sumOfLines(lines: ItemPtr): Integer;
var
a : Integer;
v : Integer;
err: Boolean;
begin
a := 0;
while lines <> nil do
begin
err := false;
v := toInt(lines^.text, err);
if not err then
a := a + v;
lines := lines^.next;
end;
sumOfLines := a;
end;
Dadurch wird die Zeichenfolge in eine Ganzzahl konvertiert und hinzugefügt, während die Zeilen Zeile für Zeile überprüft werden. Die toInt-Funktion, die eine Zeichenfolge in eine Ganzzahl konvertiert, wird auch im Programm definiert, da sie nicht im Standard-Pascal enthalten ist. Wenn eine andere Zeichenfolge als ein numerischer Wert eintrifft, wird dies anhand des zweiten Arguments err beurteilt, um keinen Fehler zu verursachen. (Daher ist dieses zweite Argument ein Referenzaufruf.)
Das ganze Programm sieht so aus:
program CalcSumApp;
type
ItemPtr = ^Item;
Item =
record
next: ItemPtr;
text: ShortString;
end;
function toInt(str: ShortString; var err: Boolean): Integer;
var
i : Integer;
n : Integer;
r : Integer;
begin
r := 0;
for i := 1 to ord(str[0]) do
begin
n := Ord(str[i]);
if (n < Ord('0')) or (n > Ord('9')) then
begin
err := true;
break;
end;
r := r * 10 + n - Ord('0');
end;
toInt := r;
end;
function toStr(n: Integer): ShortString;
var
m : Integer;
s : ShortString;
begin
s := '';
while n > 0 do
begin
m := n mod 10;
n := n div 10;
s := chr(m + ord('0')) + s;
end;
if s = '' then
s := '0';
toStr := s;
end;
procedure disposeAll(ptr: ItemPtr);
begin
if ptr <> nil then
begin
disposeAll(ptr^.next);
end;
end;
function readLines(): ItemPtr;
var
result: ItemPtr;
prevItemPtr: ItemPtr;
currItemPtr: ItemPtr;
line: ShortString;
begin
result := nil;
prevItemPtr := nil;
while true do
begin
write('>');
readln(line);
if line <> '' then
begin
if prevItemPtr = nil then
begin
new(prevItemPtr);
prevItemPtr^.text := line;
prevItemPtr^.next := nil;
result := prevItemPtr;
end
else
begin
new(currItemPtr);
currItemPtr^.text := line;
currItemPtr^.next := nil;
prevItemPtr^.next := currItemPtr;
prevItemPtr := currItemPtr;
end;
end
else
break;
end;
readLines := result;
end;
function sumOfLines(lines: ItemPtr): Integer;
var
a : Integer;
v : Integer;
err: Boolean;
begin
a := 0;
while lines <> nil do
begin
err := false;
v := toInt(lines^.text, err);
if not err then
a := a + v;
lines := lines^.next;
end;
sumOfLines := a;
end;
var
lines : ItemPtr;
sum : Integer;
begin
writeln('Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.');
lines := readLines();
sum := sumOfLines(lines);
writeln('Die Summe ist' + toStr(sum) + 'ist.');
disposeAll(lines);
end.
In Java implementieren wir es mit zwei Klassen, der CalcSumApp-Klasse und der CalcSum-Klasse. Die erste ist die CalcSumApp-Klasse für die gesamte Anwendung.
public class CalcSumApp {
public static void main(String args[]) {
CalcSum cs = new CalcSum();
CalcSumApp app = new CalcSumApp();
app.start(cs);
}
public void start(CalcSum cs) {
System.out.println("Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.");
cs.readLines();
int sum = cs.getSum();
System.out.println("Die Summe ist" + String.valueOf(sum) + "ist.");
}
}
Erstellen Sie in der Funktion "main" eine Instanz der Klasse "CalcSumApp" und der Klasse "calcSum" und rufen Sie die Methode "start" auf.
Der Inhalt der Methode "start" entspricht fast der Hauptroutine von Pascal, verarbeitet jedoch keine Daten direkt, sondern nur Objekte der Klasse "CalcSum".
Dann sieht die CalcSum-Klasse folgendermaßen aus:
class CalcSum {
List<String> list;
CalcSum () {
list = new ArrayList<String>();
}
public void readLines() {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String line;
try {
do {
System.out.print('>');
line = input.readLine();
if ("".equals(line)) {
break;
}
list.add(line);
} while (true);
} catch (IOException e) {
}
}
public int getSum() {
int sum = 0;
for (String s : list) {
try {
sum += Integer.valueOf(s).intValue();
} catch (NumberFormatException e) {
}
}
return sum;
}
}
Die Methode "readLines", die Eingaben von der Konsole akzeptiert, und die Methode "getSum", die die Summe berechnet, sind definiert. Der Inhalt entspricht fast der Funktion "readLines" und der Funktion "sumOfLines" in pascal. Die eingegebenen numerischen Daten (Liste) bleiben jedoch als Instanzvariable der Klasse "CalcSum" erhalten.
Das ganze Programm sieht so aus: Im Vergleich zu Pascal ist es sauberer, da es keinen Konvertierungsprozess wie toInt oder toStr oder einen Prozess wie disposeAll of memory release gibt und es schön ist, dass die Liste in CalcSum passt. (Obwohl es vielleicht besser ist, es "privat" zu machen)
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.NumberFormatException;
class CalcSum {
List<String> list;
CalcSum () {
list = new ArrayList<String>();
}
public void readLines() {
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String line;
try {
do {
System.out.print('>');
line = input.readLine();
if ("".equals(line)) {
break;
}
list.add(line);
} while (true);
} catch (IOException e) {
}
}
public int getSum() {
int sum = 0;
for (String s : list) {
try {
sum += Integer.valueOf(s).intValue();
} catch (NumberFormatException e) {
}
}
return sum;
}
}
public class CalcSumApp {
public static void main(String args[]) {
CalcSum cs = new CalcSum();
CalcSumApp app = new CalcSumApp();
app.start(cs);
}
public void start(CalcSum cs) {
System.out.println("Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile.");
cs.readLines();
int sum = cs.getSum();
System.out.println("Die Summe ist" + String.valueOf(sum) + "ist.");
}
}
Da dieses Beispiel einfach ist, denke ich, dass es möglich ist, CalcSum als unveränderliches Objekt zu implementieren, aber im Allgemeinen denke ich, dass bei der objektorientierten Programmierung die Objekte, die unveränderlich sein können, begrenzt sind, sodass ich nicht auf die Implementierung eingehen werde, die es unveränderlich macht. tat.
Bei Haskell sieht die Hauptroutine ziemlich gleich aus und sieht folgendermaßen aus:
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
lines <- readLines getLines
list <- sequence lines
let s = sum list
putStrLn $ "Die Summe ist" ++ (show s) ++ "ist."
Der in den Kommentaren in diesem Artikel erwähnte Teil "hSetBuffering stdout NoBuffering" war, dass die Pufferung ohne ihn nicht garantieren würde, dass die Ein- und Ausgänge in der erwarteten Reihenfolge sind. (Danke an @igrep)
getLines
ist eine Liste, die unendlich viele Zeichenfolgen empfängt und wie folgt definiert ist.
getLines :: [IO String]
getLines = do
repeat $ putStr ">" >> getLine
repeat
erstellt eine unendliche Liste. Mit anderen Worten
repeat x = [x x x x .... x ... ]
Es ist wie es ist. Es ist ein Merkmal von Hakells Verzögerungsverarbeitung, dass eine solche unendliche Liste verarbeitet werden kann, und ich wollte sie so verwenden.
readLines
ist der Vorgang des Ausschneidens des Teils, bis eine leere Zeile eingegeben wird, und ist wie folgt.
readLines :: [IO String] -> IO [IO Int]
readLines [] = return []
readLines (l:ls) = do
v <- l :: IO String
if v == ""
then return []
else do
ll <- readLines ls
case readMaybe v of
Just x -> return $ (return x):ll
Nothing -> return ll
Wenn das erste Element des Arrays ein leeres Zeichen ist, ist es das. Andernfalls,
Konvertieren Sie das erste Element in einen numerischen Wert (readMaybe
). Wenn es konvertiert werden kann, fügen Sie das konvertierte hinzu (nachverarbeitet) und geben Sie es zurück. Wenn es nicht konvertiert werden kann, konvertieren Sie den Rest (rekursiv verarbeitet). Kommt nur zurück.
list <- sequence lines
Hier ist es etwas verwirrend, aber ich konvertiere ein E / A-Array in ein E / A-Array und extrahiere dessen Inhalt. "Liste" ist also nur ein Array von Zahlen.
Ursprünglich hier
list <- mapM (>>= return) lines
Wie im Kommentar erwähnt, kann es jedoch in einem Schuss mit "squence" gelöst werden, also habe ich es geändert.
Als nächstes erfolgt die Berechnung der Gesamtsumme.
let s = sum list
(Ich werde die Geschichte der Bewertung weglassen, weil mein Verständnis falsch war.)
Das ganze Programm sieht so aus: Die Konvertierung von Monaden ist etwas mühsam und im Gegensatz zu Java wird die Variable lines
direkt behandelt, aber mit der do-Syntax kann sie wie pascal beschrieben werden.
module CalcSumApp where
import Text.Read
import System.IO
getLines :: [IO String]
getLines = do
repeat $ putStr ">" >> getLine
readLines :: [IO String] -> IO [IO Int]
readLines [] = return []
readLines (l:ls) = do
v <- l :: IO String
if v == ""
then return []
else do
ll <- readLines ls
case readMaybe v of
Just x -> return $ (return x):ll
Nothing -> return ll
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
lines <- readLines getLines
list <- sequence lines
let s = sum list
putStrLn $ "Die Summe ist" ++ (show s) ++ "ist."
Haskell versuchte auch, einen Datentyp zu erstellen und ihn objektorientiert zu kapseln. Ich denke nicht, dass es praktisch ist, aber ...
Die Hauptroutine ist
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
calcSum <- newCalcSum -- calcSum = new CalcSum()
inputStrings calcSum -- calcSum.inputString()
sum <- getSum calcSum -- sum = calcSum.getSum()
putStrLn $ "Die Summe ist" ++ (show sum) ++ "ist."
Es sieht so aus und ich denke, es ist näher an Java. Im ganzen Code
module CalcSumApp2 where
import Text.Read
import Control.Monad
import Data.IORef
import Data.Maybe
import System.IO
data CalcSum = CalcSum { getStrings :: IORef [String] }
newCalcSum :: IO CalcSum
newCalcSum = do
ref <- newIORef []
return $ CalcSum ref
inputStrings :: CalcSum -> IO ()
inputStrings calcSum = do
let truncateStrings :: [IO String] -> IO [String]
truncateStrings [] = return []
truncateStrings (x:xs) = do
s <- x
if s == ""
then
return []
else do
l <- truncateStrings xs
return $ (:l) $! s -- strict evaluation of s corresponding to `return (s:l)`
list <- truncateStrings $ repeat $ putStr ">" >> getLine
writeIORef (getStrings calcSum) list -- calcSums.strings = list
getSum :: CalcSum -> IO Int
getSum calcSum = do
list <- readIORef (getStrings calcSum) :: IO [String] -- list = calcSum.strings
let nums = catMaybes $ readMaybe <$> list :: [Int]
return $ sum nums
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStrLn "Bitte geben Sie die Nummer ein. Es endet mit einer Leerzeile."
calcSum <- newCalcSum -- calcSum = new CalcSum()
inputStrings calcSum -- calcSum.inputString()
sum <- getSum calcSum -- sum = calcSum.getSum()
putStrLn $ "Die Summe ist" ++ (show sum) ++ "ist."
Was ich hier sagen wollte ist, dass das Objekt (wie das Ding) "IORef" ist. Ich denke, dass Objekte in erster Linie nichts mit E / A zu tun haben, aber in meinen Augen sind Objekte Dinge, die einen Bereich im Heapspeicher zum Lesen und Schreiben (ein Bild wie DB) getrennt von der Anwendung zuweisen, also "IORef" Ich dachte, es würde schön kommen.
Objekte in objektorientierten Sprachen sind nicht nur IORef-ähnlich (gemeinsam genutzt und veränderbar), sondern auch nicht gemeinsam genutzte Objekte, unveränderliche Objekte, unveränderliche und seitenlose Objekte (z. B. keine E / A-Eingabe / Ausgabe) und Nebenwirkungen. Ich denke, es gibt verschiedene Dinge wie große Objekte (Objekte, die die Verarbeitung umschließen, die mit dem externen Server interagiert), aber ich konnte nicht gut darüber nachdenken, deshalb werde ich dieses Beispiel hier nur vorstellen. Ich werde es behalten.
Standard Pascal hat nur wenige Funktionen und Sie müssen "toInt" oder "toStr" selbst implementieren oder den Speicher selbst freigeben, aber dieser (umständliche) Vorgang ist abgeschlossen. Wenn ich es schreibe, fühle ich mich gerne wie "Oh, ich programmiere." Ich habe das Gefühl, ich spiele mit einem manuellen Auto anstelle eines automatischen Autos. In diesen Tagen ist Go so.
Obwohl Java auf verschiedene Weise kritisiert wurde, denke ich, dass es eine gute Sprache ist, die einfach zu bedienen ist. Ich bin der Meinung, dass die objektorientierte Programmiersprache besser für Metaprogrammierung wie Ruby und das dynamische Erstellen von Objekten wie JavaScript geeignet ist, aber es besteht ein Sicherheitsgefühl, dass die Klasse statisch bestimmt wird. Hört sich gut an. Ich mag Java 1.5 oder so und es ist eine Schande, dass es mit so vielen anderen Paradigmen kompliziert wird.
Haskell hat viel zu lernen und kämpft, aber ich bin sehr glücklich, wenn es funktioniert. Es gibt viele Funktionen und es ist schwer zu verstehen und sich zu erinnern, aber es scheint eine große Sache zu sein, wenn man weiß, wie man es benutzt, und es ist schön, eine statische Lösung zu haben. Ich denke, es ist gut, vorsichtig mit dem Typ zu sein.
In diesem einfachen Beispiel ändert sich an keiner Implementierung viel, aber ich habe mich gefragt, ob es noch einige Funktionen für jede Sprache gibt.
Die Anzahl der Programmiersprachen ist heutzutage ein Multi-Paradigma, weshalb ich es für wichtig halte, die Grundlagen der Single-Paradigma-Sprachen zu lernen.
Wenn Sie Fehler oder Unzulänglichkeiten haben, kommentieren Sie bitte.
Recommended Posts