I tried a functional language with Python

I recently touched Clojure for some reason.

For those who have been using procedural languages such as C and Python for nearly eight years, at first there was some confusion about "functional languages", but on the other hand, Python incorporates many of the advantages of functional languages. I also noticed.

Therefore, I would like to try out how much functional language-like things can be done with Python by referring to Clojure's sample.

This time, ayato-p published "Clojure's Japanese Guide" Idioms Let's implement the contents of /clojure-beginner/idioms/index.html) in Python. The version of Python at hand is 3.6.1.

Group elements from multiple collections by index

[0 1 2 3 4]
[:a :b :c :d :e]

There is no "keyword" in Python, so use a string instead. It's easy with zip.

a = [0, 1, 2, 3, 4]
b = [":a" ":b" ":c" ":d" ":e"]
zip(a, b)
# -> <zip at 0x111a14548>

Oops, Python 3 makes heavy use of iterators. It is not evaluated as it is, so if you want to see each element, you need to enclose it in a list.

list(zip(a, b))
# -> [(0, ':a'), (1, ':b'), (2, ':c'), (3, ':d'), (4, ':e')]

Convert the map to a flat sequence

{:name    "ayato-p"
 :age     "24"
 :address "Japan"}

Concatenate (key, value) pairs in the dictionary using the chain in the itertools module. The asterisk "*" is an operator for passing list elements as function arguments.

import itertools

d = {":name":    "ayato-p",
     ":age":     "24",
     ":address": "Japan"}

list(itertools.chain(*d.items()))
# -> [':name', 'ayato-p', ':age', '24', ':address', 'Japan']

I want to pass sequence data to a function that accepts variadic arguments

(def v ["foo" "bar" "baz"])

(defn f [& args]
  (clojure.string/join ", " args))

The way to pass arguments is the same as we did above. You also use an asterisk to define a function that accepts variadic arguments.

v = ["foo", "bar", "baz"]

def f(*args):
    return ", ".join(args)

f(*v)
# -> 'foo, bar, baz'

When passing a dictionary, use two asterisks.

m = {"name": "ayato-p", "age": 24}

def g(name, age):
    return "name:" + name + ", age:" + str(age)

g(**m)
# -> 'name:ayato-p, age:24'

Apply a function to all elements of the sequence and discard nil

(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)

Just use the list comprehension with the conditional expression.

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]

I want to know if a value is boolean

Use is instance.

isinstance(True, bool) # -> True
isinstance(False, bool) # -> True
isinstance("", bool) # -> False
isinstance(None, bool) # -> False
isinstance(0, bool) # -> False
isinstance(1, bool) # -> False

If a non-nil value is found among multiple candidates, the value is returned.

This is the same for Clojure and Python.

None or "ayato-p"
# -> "ayato-p"

However, everything that becomes False when booled (None, 0, False, empty list, etc.) applies, so I think it is better to use if seriously.

I want to see if the sequence is empty

You can measure the length with len or evaluate it with if as it is. If you enclose it in bool, True / False will be returned depending on the presence or absence of the contents.

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

I want to assoc / dissoc only when the conditions are met for the map, and return it as it is otherwise

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

using if and 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}

This is the first time I've used the dict function in this way. When actually writing a program in Python, I think that it is more common to rewrite the contents of dict before using it.

I want to stop reduce in the middle

(reduce (fn [acc x]
          (if (zero? x)
            (reduced 0)
            (* acc x)))
        1
        (cycle [9 8 7 6 5 4 3 2 1 0]))

Since reduced is not in Python, we use exceptions to implement similar functionality.

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

I want to apply a function (map) to all the keys (values) of a map

Easy with dictionary comprehension.

m = {"key1": 1,
     "key2": 2,
     "key3": 3}

{":" + k: v for k, v in m.items()}
# -> {':key1': 1, ':key2': 2, ':key3': 3}

I want to drop elements from a vector based on an index

If you want to change the data in the list, you can use pop. You can also create a new list using enumerate and list comprehensions.

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]

I want to make an instance of java.util.LinkedList a vector

Ordinary Python cannot handle Java instances. There seems to be Python implemented in Java called Jython, but it seems that development is stagnant, so skip it.

I want to keep a value that updates several times between loops

This is usually a for loop.

I want a simple database that can be referenced throughout the program

Skip it because the quote source says "I don't recommend it very much".

Remove falsy values from the list

(filter identity [nil false true 1 "hello" [1 2] {:foo 1} :hoge])
;; (true 1 "hello" [1 2] {:foo 1} :hoge)

This is also a list comprehension

[x for x in [None, False, True, 1, "hello", [1, 2], {":foo": 1}] if x]
# -> [True, 1, 'hello', [1, 2], {':foo': 1}]

Index the list of objects

As already mentioned above, we will use enumerate.

list(enumerate(["a", "b", "c", "d", "e"]))
# -> [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]

By doing this, you can use it as follows.

[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']

Get the first one that matches the condition from the sequence

(defn find-first [pred coll]
  (first (drop-while (complement pred) coll)))

It seems like you should write a sloppy for loop, but I won't write it! !! Create a generator and use next to retrieve the first element.

l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
next(x for x in l if x > 4)
# -> 5

An example of extracting the smallest prime number larger than a certain natural number by trial division

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

(Added on July 20, 2017) In primality test, it is more concise to use "all" which returns true when all elements of iterable are true.

def isprime(x):
    return all(x % n for n in range(2, int(x ** 0.5) + 1))

Summary

Could you give us a quick comparison between Clojure and Python? I'm new to Clojure, so there may be a lot of things that Clojure can and Python can't.

However, if you look at it like this, you can see that in Python, by using list comprehensions and generator expressions, it is possible to write functional language-like processing very compactly without writing extra function names.

You don't have to force yourself to use the generator as in the last example, but if you look at it to the extent that you can do something like this, it may help you write code someday.

See you soon~.

Recommended Posts

I tried a functional language with Python
I tried fp-growth with python
I tried scraping with Python
I tried gRPC with Python
I tried scraping with python
I tried to draw a route map with Python
I tried to automatically generate a password with Python3
I tried web scraping with python.
I made a fortune with Python.
I tried running prolog with python 3.8.2.
I made a daemon with Python
I tried SMTP communication with Python
I tried a stochastic simulation of a bingo game with Python
I tried to divide with a deep learning language model
I tried scraping Yahoo News with Python
I made a character counter with Python
I drew a heatmap with seaborn [Python]
I tried sending an email with python.
I tried non-photorealistic rendering with Python + opencv
I tried recursion with Python ② (Fibonacci sequence)
What I did with a Python array
I made a Hex map with Python
I tried natural language processing with transformers.
I made a roguelike game with Python
I made a simple blackjack with Python
I made a configuration file with Python
#I tried something like Vlookup with Python # 2
I made a neuron simulator with Python
[5th] I tried to make a certain authenticator-like tool with python
[2nd] I tried to make a certain authenticator-like tool with python
[3rd] I tried to make a certain authenticator-like tool with python
[Python] A memo that I tried to get started with asyncio
I tried to create a list of prime numbers with python
I tried to make a periodical process with Selenium and Python
I tried to make a 2channel post notification application with Python
I tried to make a todo application using bottle with python
[4th] I tried to make a certain authenticator-like tool with python
[1st] I tried to make a certain authenticator-like tool with python
I tried Python> decorator
I tried "smoothing" the image with Python + OpenCV
I tried hundreds of millions of SQLite with python
I made a competitive programming glossary with Python
I made a GUI application with Python + PyQt5
I tried "differentiating" the image with Python + OpenCV
I tried playing a typing game in Python
I made a Twitter fujoshi blocker with Python ①
I tried L-Chika with Raspberry Pi 4 (Python edition)
[Python] I played with natural language processing ~ transformers ~
[Python] I made a Youtube Downloader with Tkinter.
I tried Jacobian and partial differential with python
I tried to get CloudWatch data with Python
I tried using mecab with python2.7, ruby2.3, php7
[Memo] I tried a pivot table in Python
I tried function synthesis and curry with python
I tried to output LLVM IR with Python
I tried "binarizing" the image with Python + OpenCV
I tried using Pythonect, a dataflow programming language.
I tried reading a CSV file using Python
I tried running faiss with python, Go, Rust
I tried to automate sushi making with python
I tried playing mahjong with Python (single mahjong edition)