四則演算 - Lisp
コンスセルとconsとcarとcdr
Lispはすべてリストでできている。また、リストとリストを繋ぐ役目にコンスセルというものがある。これはすごい面白い考え方だと思った
コンスセルは2つの部屋(セル)でできている。1つ目のセル(CARと呼ばれる)にはデータなどを指し、2つ目のセル(CDRと呼ばれる)には別のコンスセルやnil
というリストの終わりを示す特別なシンボルを指すことができる。
また、データそのものが入るわけではなく、データの参照を指し示すようになっている。ポインタみたいな?
Lispではコンスセルの連なりとリストは全く同じものとして扱われる。
コンスセルについてはAbout Cons Cellsがわかりやすかった
Lispのプログラムからコンスセルを扱うにはcons
関数を使う。ほかにもcar
とcdr
がある。
cons関数
> (cons 1 2) (1 . 2)
これは、1と2をコンスセルで繋いでいますよっていうこと。リストと区別するために、.
が入っている
> (cons 1 'nil) (1)
第2引数にリストの終りを示す'nil
を渡すと、通常のリストのような表示になる。(1 . 'nil)
と同じ意味。Lispはできるだけ、コンスセルではなくリストとして見せてくれている!
> (cons 1 ()) (1)
Common Lispでは、空のリスト()
とシンボル'nil
は同じ意味となっている。そのためこのようにも書ける。
「空のリストに1を追加したら、1だけのリストになる」と考えればしっくりきた。
どんなリストもコンスセルで表せる!
> '(1 2 3) (1 2 3) > (cons 1 (cons 2 (cons 3 'nil))) (1 2 3) > (cons 1 (cons 2 (cons 3 ()))) (1 2 3)
car関数とcdr関数
コンスセルを扱うための関数であるcar(カー)とcdr(クダー)。それぞれ、コンスセルの部屋の構造と一致している。
car
で1つ目のコンスセルのCARセル。cdr
で1つ目のコンスセルに紐づくデータが取得できる。
> (car '(dog cat chicken)) DOG > (cdr '(dog cat chicken)) (CAT CHICKEN) > (cdr (cons 'b 'nil)) NIL
組み合わせることで取得したい場所を取得できる。ちょっと複雑になるけど。。。
例)2番目の要素を取得する
> (car (cdr '(dog cat chicken))) CAT
cdr
で2番目以降の要素を取得し、それに対してcar
で1番目の要素(2番目)を取得する
一度に取得できる関数も用意されている
> (cadr '(dog cat chicken)) CAT > (cadr '(dog cat chicken)) CAT > (cddr '(dog cat chicken)) (CHICKEN) > (caddr '(dog cat chicken)) CHICKEN > (cdddr '(dog cat chicken)) NIL
各順番のとおりにa
かd
を書けばいいのかも。(car (cdr '(...)))
だから、(cadr '(...))
みたいに。
最後のコンスセルにはnilが入っていることを忘れずに!!!
cdadadr
をやろうとしたら、そんなの無いよ!!!っていわれた。最大4つまでらしい。
> (cdadadr '((peas carrots tomatoes) (pork beef chicken))) *** - EVAL: undefined function CDADADR
list関数
list関数を使うと簡単にリストが作れる。
以下の3つは同じ意味!!
> (list 'dog 'cat 'chicken) (DOG CAT CHICKEN) > '(dog cat chicken) (DOG CAT CHICKEN) > (cons 'dog (cons 'cat (cons 'chicken ()))) (DOG CAT CHICKEN)
nilを含まないデータでcdrを使うと
なんかおもしろいと思った。
> (cons 'a (cons 'b 'c)) (A B . C) > (cdr (cons 'a (cons 'b 'c))) (B . C) > (cddr (cons 'a (cons 'b 'c))) C
.
は要素なのかな!?って思ってみたけど、そんなことはなかった。。。
listとクオートの快適さを実感した
複雑なリストを作るときにちょっと手こずった。
> (cons (cons 'peas (cons 'carrots (cons 'tomatoes ()))) (cons 'pork (cons 'beef (cons 'chicken ())))) ((PEAS CARROTS TOMATOES) PORK BEEF CHICKEN)
((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN))
ってなってほしい!!
> (cons (cons 'peas (cons 'carrots (cons 'tomatoes ()))) (cons (cons 'pork (cons 'beef (cons 'chicken ()))) ())) ((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN))
できたけど、ややこしいよ!!!!!!
これをやったあとにlist
やクオートを使うとなんと楽なことか
> (list (list 'peas 'carrots 'tomatoes) (list 'pork 'beef 'chicken)) ((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN)) > '((peas carrots tomatoes) (pork beef chicken)) ((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN))
あぁ、楽すぎる...まじでやばい
Lispを始める
Lispになぜか興味を持ってしまい、本まで買ってしまった。
Lispの魅力は
たぶん、もっとあった気がする
Lispの種類
ANSI Common LispとSchemeというのが有名らしい。最近見たのはClojureっていう言語も有名かも?
clispをインストールする
clispというCommon Lispで書かれたコンパイラを使って、書いていく
CLISP - an ANSI Common Lisp Implementation
brew install clisp
clisp --version
さぁ!Lispをはじめるぞ!!
roswellっていうのもあるらしいけど、とりあえず、最初はclispでやってみる。基礎がわかってきたらroswellでやってみよう。
インストール完了時に出てきた文字がちょっと気になった
==> clisp Emacs Lisp files have been installed to: /usr/local/share/emacs/site-lisp/clisp
Emacs!?なんですと!?emacs
とコマンドを打ってみたら、Emacsが起動した。終了の仕方わからなくておどおどしてた。C-x C-c
で終了できた
参考文献
gzipモジュールを使った圧縮と展開 - Python
vcrpyでは、レスポンスの量が多い場合、圧縮されて(バイナリになって)カセットに格納される。どうやって圧縮されたデータを扱うのか調べてみたら標準モジュールのgzipモジュールでできそうだったため、メモしておく
圧縮にはgzip.compress()
メソッド。展開にはgzip.decompress()
メソッド。それぞれ、バイト配列を扱う。
文字列を圧縮して、展開して、取得してみた
>>> import gzip >>> t_bytes = bytes('あいう123abc', 'utf-8') >>> t_bytes b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86123abc' >>> compressed_bytes = gzip.compress(t_bytes) # 圧縮 >>> compressed_bytes b'\x1f\x8b\x08\x00G::[\x02\xff{\xdc\xd8\xf4\xb8\xb1\xe5qc\x9b\xa1\x91qbR2\x00\xe1\xc7\x079\x0f\x00\x00\x00' >>> decompressed_bytes = gzip.decompress(compressed_bytes) # 展開 >>> decompressed_bytes b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86123abc' >>> decompressed_bytes.decode('utf-8') 'あいう123abc'
gzip.compress()
にはバイト配列(bytes)を渡さないといけないため、bytes(str, 'utf-8')
でバイト配列を作成する
gzip.compress()
に作成したバイト配列を渡して、圧縮する。
展開にはgzip.decompress()
を使う。圧縮されているバイト配列を渡すことで、展開してくれる
展開したバイト配列をbytes.decode()
で文字列に変換し、終了
これで、一応、圧縮と展開ができるようになった
参考文献
mypyとjedi-vimで補完を強くする - Vim
Pythonで型アノテーションを使いたくなって、どうせならVimでも補完できるようにしたいと思って、やってみた
Python3が有効になっているか確認
まずはVimでPython3が有効になっているか確認する
もし有効になっていない場合、ここを参考にして、有効にする
$ pip3 install -U flake8 mypy-lang jedi
neomakeを入れる
非同期で型、スタイルチェックを行うにはneomakeを使う
neomake/neomake: Asynchronous linting and make framework for Neovim/Vim
call dein#add('neomake/neomake') ... let g:neomake_python_enabled_makers = ['python', 'flake8', 'mypy'] autocmd FileType python setlocal omnifunc=jedi#completions if !exists('g:neocomplete#force_omni_input_patterns') let g:neocomplete#force_omni_input_patterns = {} endif let g:neocomplete#force_omni_input_patterns.python = '\%([^. \t]\.\|^\s*@\|^\s*from\s.\+import \|^\s*from \|^\s*import \)\w*'
syntasticを入れる
なんとなくsyntasticも入れた
vim-syntastic/syntastic: Syntax checking hacks for vim
call dein#add('scrooloose/syntastic') ... let g:syntastic_check_on_open = 1 let g:syntastic_python_checkers = ['flake8', 'pep257', 'mypy'] let g:syntastic_python_flake8_args = '--max-line-length=120'
入れるときにエラーとかのメモ
以下の記述を書かないとエラーになった
if !exists('g:neocomplete#force_omni_input_patterns') let g:neocomplete#force_omni_input_patterns = {} endif
こんなエラー
E121: Undefined variable: g:neocomplete#force_omni_input_patterns
あと、無効にしていたから、動作しなくて焦った
let g:jedi#completions_enabled = 0
jedi#completetions_enabled
は、jedi-vimの補完を有効にするかどうかってことらしい。deoplete-jediを使うときにはこれを無効(0)にするらしい
参考文献
GETリクエストのパラメータの空白はそのままでいい requests - Python
requests.get()
でGETリクエストを送信するときのパラメータに空白が入るときはどうすればいいのかわからなかったが、そのまま渡していいことがわかった
import requests params = {'aaa': '111 あああ', 'bbb': '222 いいい'} res = requests.get('http://google.com', params=params)
リクエストされるURLは'http://google.com/?aaa=111+%E3%81%82%E3%81%82%E3%81%82&bbb=222+%E3%81%84%E3%81%84%E3%81%84'
となる
requestsのソースを追っていったらなんかわかった気がするからメモしておく
パラメータがエンコードされるまでのスタックトレース(下から上に呼び出している)
urlencode() urllib.parse.py:906 _encode_params() models.py:105 prepare_url() [PreparedRequest] models.py:423 prepare() [PreparedRequest] models.py:305 prepare_request() [Session] sessions.py:437 request() [Session] sessions.py:494 request() api.py:58 get() api.py:72
urllib.parse.urlencode()
が最終的に呼ばれていたため、' 'が'+'になっているということ!!あまり気にしなくても良かったのかも...
参考文献
requests.apiモジュール - Python
https://github.com/requests/requests/blob/00fd4c8eb4ac0fd7b8f8d76bbf15ab06351c052c/requests/api.py#L16-L58 から重要なところを引っ張ってきた
def request(method, url, **kwargs): with sessions.Session() as session: return session.request(method=method, url=url, **kwargs)
リクエストを送る前に毎回Sessionを生成しているということ。また、with
を使ってるからそのセッションは閉じられている。セッションは必ず閉じないといけない。
使用例
セッションを使うときはrequests.Session
を使い、セッションを使わないときはrequests.api
を使うみたいな?
import requests class Requester(object): _session = None def __init__(self, requests_session=True): if requests_session: self._session = requests.Session() else: from requests import api self._session = api def internal_call(self): r = self._session.request('GET', 'http://google.com') print(r.status_code) if __name__ == '__main__': requester = Requester(False) requester.internal_call() requester = Requester() requester.internal_call()
実行結果
200 200