Ich habe kürzlich Clojure aus irgendeinem Grund berührt.
Für diejenigen, die seit fast acht Jahren prozedurale Sprachen wie C und Python verwenden, gab es zunächst einige Verwirrung über "funktionale Sprachen", aber andererseits enthält Python viele der Vorteile funktionaler Sprachen. Ich habe es auch bemerkt.
Daher möchte ich anhand des Clojure-Beispiels ausprobieren, wie viel funktionale sprachähnliche Dinge mit Python erledigt werden können.
Dieses Mal veröffentlichte ayato-p Idioms von "Clojure's Japanese Guide". Lassen Sie uns den Inhalt von /clojure-beginner/idioms/index.html) in Python implementieren. Die vorliegende Version von Python ist 3.6.1.
[0 1 2 3 4]
[:a :b :c :d :e]
Da es in Python kein "Schlüsselwort" gibt, wird stattdessen eine Zeichenfolge verwendet. Mit Reißverschluss ist das ganz einfach.
a = [0, 1, 2, 3, 4]
b = [":a" ":b" ":c" ":d" ":e"]
zip(a, b)
# -> <zip at 0x111a14548>
Hoppla, Python 3 verwendet stark Iteratoren. Es wird nicht so ausgewertet, wie es ist. Wenn Sie also jedes Element sehen möchten, müssen Sie es in eine Liste aufnehmen.
list(zip(a, b))
# -> [(0, ':a'), (1, ':b'), (2, ':c'), (3, ':d'), (4, ':e')]
{:name "ayato-p"
:age "24"
:address "Japan"}
Verketten Sie (Schlüssel, Wert) Paare im Wörterbuch mithilfe der Kette im itertools-Modul. Das Sternchen "*" ist ein Operator zum Übergeben von Listenelementen als Funktionsargumente.
import itertools
d = {":name": "ayato-p",
":age": "24",
":address": "Japan"}
list(itertools.chain(*d.items()))
# -> [':name', 'ayato-p', ':age', '24', ':address', 'Japan']
(def v ["foo" "bar" "baz"])
(defn f [& args]
(clojure.string/join ", " args))
Die Übergabe der Argumente ist die gleiche wie oben. Sie verwenden auch ein Sternchen, wenn Sie eine Funktion definieren, die Argumente variabler Länge akzeptiert.
v = ["foo", "bar", "baz"]
def f(*args):
return ", ".join(args)
f(*v)
# -> 'foo, bar, baz'
Verwenden Sie beim Übergeben eines Wörterbuchs zwei Sternchen.
m = {"name": "ayato-p", "age": 24}
def g(name, age):
return "name:" + name + ", age:" + str(age)
g(**m)
# -> 'name:ayato-p, age:24'
(def people [{:name "ayato_p" :age 11}
{:name "alea12" :age 10}
{:name "zer0_u"}])
(remove nil? (map :age people)) ;(11 10)
(keep :age people) ;(11 10)
Verwenden Sie einfach die Listeneinschlussnotation mit dem bedingten Ausdruck.
people = [{":name": "ayato_p", ":age": 11},
{":name": "alea12", ":age": 10},
{":name": "zer0_u"}]
[x[":age"] for x in people if ":age" in x]
# -> [11, 10]
Verwendung ist Instanz.
isinstance(True, bool) # -> True
isinstance(False, bool) # -> True
isinstance("", bool) # -> False
isinstance(None, bool) # -> False
isinstance(0, bool) # -> False
isinstance(1, bool) # -> False
Dies gilt auch für Clojure und Python.
None or "ayato-p"
# -> "ayato-p"
Es gilt jedoch alles, was beim Boolen falsch wird (Keine, 0, Falsch, leere Liste usw.). Ich denke, es ist besser, es zu verwenden, wenn es ernst ist.
Sie können die Länge mit len messen oder mit if bewerten, wie es ist. Wenn Sie es in bool einschließen, wird True / False abhängig vom Vorhandensein oder Fehlen des Inhalts zurückgegeben.
ev = []
v = [1, 2]
if ev:
print("not empty")
else:
print("empty")
# -> empty
if v:
print("not empty")
else:
print("empty")
# -> not empty
bool(ev)
# -> False
bool(v)
# -> True
(def m {:foo 1 :bar 2})
(cond-> m
true (assoc :baz 3)) ;{:foo 1, :bar 2, :baz 3}
(cond-> m
false (assoc :baz 3)) ;{:foo 1, :bar 2}
mit if und dict
m = {":foo": 1, ":bar": 2}
dict(m, **{":baz": 3}) if True else m
# -> {':bar': 2, ':baz': 3, ':foo': 1}
dict(m, **{":baz": 3}) if False else m
# -> {':bar': 2, ':foo': 1}
Dies ist das erste Mal, dass ich die Diktierfunktion auf diese Weise verwende. Wenn Sie tatsächlich ein Programm in Python schreiben, ist es meiner Meinung nach üblicher, den Inhalt von dict vor der Verwendung neu zu schreiben.
(reduce (fn [acc x]
(if (zero? x)
(reduced 0)
(* acc x)))
1
(cycle [9 8 7 6 5 4 3 2 1 0]))
Da Reduced nicht in Python enthalten ist, verwenden wir Ausnahmen, um ähnliche Funktionen zu implementieren.
import functools
class Reduced(Exception):
def __init__(self, data):
super().__init__()
self.data = data
def myreduce(f, it):
try:
return functools.reduce(f, it)
except Reduced as e:
return e.data
def mymultiply(acc, x):
if x == 0:
raise Reduced(0)
return acc * x
myreduce(mymultiply, [9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
# -> 0
Einfach, wenn Sie die Wörterbucheinschlussnotation verwenden.
m = {"key1": 1,
"key2": 2,
"key3": 3}
{":" + k: v for k, v in m.items()}
# -> {':key1': 1, ':key2': 2, ':key3': 3}
Wenn Sie die Daten in der Liste ändern möchten, können Sie pop verwenden. Sie können auch eine neue Liste mit der Aufzählungs- und Listeneinschlussnotation erstellen.
l = [9, 8, 7, 6, 5, 4, 3, 2, 1]
l.pop(5)
l
# -> [9, 8, 7, 6, 5, 3, 2, 1]
l = [9, 8, 7, 6, 5, 4, 3, 2, 1]
[x for i, x in enumerate(l) if i != 5]
# -> [9, 8, 7, 6, 5, 3, 2, 1]
Normales Python kann keine Java-Instanzen verarbeiten. In Java scheint ein Python namens Jython implementiert zu sein, aber die Entwicklung scheint zu stagnieren. Überspringen Sie es also.
Dies ist normalerweise eine for-Schleife.
Überspringen Sie es, weil die Quelle sagt "Ich empfehle es nicht sehr".
(filter identity [nil false true 1 "hello" [1 2] {:foo 1} :hoge])
;; (true 1 "hello" [1 2] {:foo 1} :hoge)
Dies ist auch eine Listeneinschlussnotation
[x for x in [None, False, True, 1, "hello", [1, 2], {":foo": 1}] if x]
# -> [True, 1, 'hello', [1, 2], {':foo': 1}]
Wie bereits oben erwähnt, werden wir enumerate verwenden.
list(enumerate(["a", "b", "c", "d", "e"]))
# -> [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
Auf diese Weise können Sie es wie folgt verwenden.
[str(i) + " is " + x for i, x in enumerate(["a", "b", "c", "d", "e"])]
# -> ['0 is a', '1 is b', '2 is c', '3 is d', '4 is e']
(defn find-first [pred coll]
(first (drop-while (complement pred) coll)))
Es scheint, als sollten Sie eine schlampige for-Schleife schreiben, aber ich werde sie nicht schreiben! !! Erstellen Sie einen Generator und verwenden Sie next, um das erste Element abzurufen.
l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
next(x for x in l if x > 4)
# -> 5
Ein Beispiel für das Extrahieren der kleinsten Primzahl, die größer als eine bestimmte natürliche Zahl ist, durch Versuchsteilung
import itertools
def isprime(x):
return next((0 for n in range(2, int(x ** 0.5) + 1)
if x % n == 0), 1)
next(x for x in itertools.count(1000) if isprime(x))
# -> 1009
(Hinzugefügt am 20. Juli 2017) Es ist prägnanter, "all" zu verwenden, was true zurückgibt, wenn alle Elemente von iterable im primären Urteil wahr sind.
def isprime(x):
return all(x % n for n in range(2, int(x ** 0.5) + 1))
Können Sie uns einen schnellen Vergleich zwischen Clojure und Python geben? Da ich ein Anfänger von Clojure bin, kann Clojure viele Dinge tun, Python jedoch nicht.
Wenn Sie es jedoch so betrachten, können Sie sehen, dass es in Python mithilfe der Listeneinschlussnotation und der Generatorausdrücke möglich ist, eine funktionale sprachähnliche Verarbeitung sehr kompakt zu schreiben, ohne zusätzliche Funktionsnamen zu schreiben.
Sie müssen sich nicht zwingen, den Generator wie im letzten Beispiel zu verwenden, aber wenn Sie ihn so weit betrachten, dass Sie so etwas tun können, kann es Ihnen helfen, eines Tages Code zu schreiben.
Bis bald ~.
Recommended Posts