2次元のリストの指定した座標を含むブロックの座標を列挙する

タイトルがわかりにくいな。
ホントは図を描けばいいのかもしれないけど、まず、2次元のリストを考える。で、そのリストの中に3×3のブロックが並んでいると考えてほしい。今日やりたいのは、ある座標(インデックス、0始まり、2次元なので当然2つの整数)を与えたときに、その座標を含むブロックの全座標を列挙したいってこと。
最初、どうやったらいいか悩んだけど、出来てみれば案外簡単だった。

module Main where

import System.Environment (getArgs)

block :: Int -> Int -> [(Int, Int)]
block i j = [(x,y)| x <- section i, y <- section j]

section :: Int -> [Int]
section n = [s..e]
  where
    s = n `div` 3 * 3
    e = s + 2

main :: IO ()
main = do
  args <- getArgs
  let i = read (args !! 0) :: Int
  let j = read (args !! 1) :: Int
  print $ block i j

実行例:

takatoh@apostrophe $ runhaskell block.hs 4 2
[(3,0),(3,1),(3,2),(4,0),(4,1),(4,2),(5,0),(5,1),(5,2)]
takatoh@apostrophe $ runhaskell block.hs 1 5
[(0,3),(0,4),(0,5),(1,3),(1,4),(1,5),(2,3),(2,4),(2,5)]

こんな感じ。

bottleアプリをuWSGIで動かす

やってみよう!

今回動かすアプリ

単純に、指定されたファイルをダウンロードさせるだけのアプリを書いた。

# encoding: utf-8

from bottle import route, static_file, abort, run, default_app
import os

# root

@route('/')
def index():
    abort(404, "File not found.")

# /path/to/file

@route('/')
def static(path):
    file_path = os.path.join("./storage", path)
    if os.path.isfile(file_path):
        return static_file(path, root='./storage', download=True)
    else:
        abort(404, "File not found.")

if __name__ == '__main__':
    run(host='localhost', port='8080', debug=True, reloader=True)
else:
    application = default_app()

run 関数が直接 python index.py したとき用の記述で、application = default_app() が uWSGI 用の記述。

下準備

uWSGI で動かす前に、実行用の環境を env という名前で作っておく。

takatoh@apostrophe $ ls
index.py  requirements.txt
takatoh@apostrophe $ virtualenv env
New python executable in env/bin/python
Installing setuptools, pip, wheel...done.
takatoh@apostrophe $ source env/bin/activate
(env)takatoh@apostrophe $ pip install -r requirements.txt
Collecting bottle==0.12.10 (from -r requirements.txt (line 1))
/home/takatoh/w/myapp/env/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
  Using cached bottle-0.12.10-py2-none-any.whl
Installing collected packages: bottle
Successfully installed bottle-0.12.10
/home/takatoh/w/myapp/env/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
You are using pip version 7.1.2, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
(env)takatoh@apostrophe $ deactivate

それから、storage という名前のディレクトリを作って、ファイル(101_ObjectCategories.tar.gz)を入れておく。これをダウンロードしてみようってわけだ。

uWSGIで起動

まずは、コマンドラインから起動してみる。

takatoh@apostrophe $ uwsgi --http :8080 --wsgi-file index.py -H env

ダウンロードできるか、テスト。

takatoh@apostrophe $ wget http://localhost:8080/101_ObjectCategories.tar.gz
--2016-11-24 20:33:35--  http://localhost:8080/101_ObjectCategories.tar.gz
localhost (localhost) をDNSに問いあわせています... 127.0.0.1
localhost (localhost)|127.0.0.1|:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 131740031 (126M) [application/x-tar]
`101_ObjectCategories.tar.gz' に保存中

100%[======================================>] 131,740,031 68.1MB/s   時間 1.8s 

2016-11-24 20:33:37 (68.1 MB/s) - `101_ObjectCategories.tar.gz' へ保存完了 [131740031/131740031]

出来た。以前、Flask アプリを動かした時より簡単だな。

設定ファイルで起動

uWSGI 用の設定ファイル。

[uwsgi]
uid = takatoh
gid = takatoh
http = :8080
venv = /home/takatoh/w/myapp/env
wsgi-file = /home/takatoh/w/myapp/index.py
master=true
pidfile=/home/takatoh/w/myapp/myapp.pid
logger=file:/home/takatoh/w/myapp/myapp.log

起動。

takatoh@apostrophe $ uwsgi uwsgi.ini
[uWSGI] getting INI configuration from uwsgi.ini

ダウンロードのテスト。

takatoh@apostrophe $ wget http://localhost:8080/101_ObjectCategories.tar.gz
--2016-11-25 08:35:08--  http://localhost:8080/101_ObjectCategories.tar.gz
localhost (localhost) をDNSに問いあわせています... 127.0.0.1
localhost (localhost)|127.0.0.1|:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 131740031 (126M) [application/x-tar]
`101_ObjectCategories.tar.gz' に保存中

100%[======================================>] 131,740,031 28.6MB/s   時間 4.5s 

2016-11-25 08:35:13 (27.8 MB/s) - `101_ObjectCategories.tar.gz' へ保存完了 [131740031/131740031]

OK。うまくいった!

Windows 10環境のrubygemsでSSLエラーが発生した場合の対処法

新しい PC のセットアップ中、rubygems でエラーが発生した。環境は次の通り。

  • Windows 10 Home
  • Ruby 2.3.1 (RubyInstaller)
  • gem 2.5.1

発生したエラーは:

^o^ > gem install acm
ERROR:  Could not find a valid gem 'acm' (>= 0), here is why:
          Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://api.rubygems.org/specs.4.8.gz)

どうも、SSLのエラーらしい。

で、いろいろとググりながら試した結果、次の手順でうまく回避できたのでメモしておく。

rubygemsのアップデート

普通なら gem コマンドでアップデートできるけど、それが使えないので rubygems.org からダウンロードしてアップデートする。
 cf. rubygems-update 2.6.8
上のリンクから、rubygems-update の最新版 2.6.8 をダウンロード、インストールする。

^o^ > gem install rubygems-update-2.6.8.gem --local
Successfully installed rubygems-update-2.6.8
Parsing documentation for rubygems-update-2.6.8
Installing ri documentation for rubygems-update-2.6.8
Done installing documentation for rubygems-update after 39 seconds
1 gem installed

^o^ > gem -v
2.5.1

インストールしただけではアップデートされないので、update_rubygems コマンドでアップデートする。

^o^ > update_rubygems
RubyGems 2.6.8 installed
Parsing documentation for rubygems-2.6.8
Installing ri documentation for rubygems-2.6.8
(以下略)

長々とメッセージが出るけど、これでアップデートできた。

^o^ > gem -v
2.6.8

確認

これで、正常に使えるようになったはず。

^o^ > gem install acm
Fetching: thor-0.19.1.gem (100%)
Successfully installed thor-0.19.1
Fetching: acm-0.4.2.gem (100%)
Successfully installed acm-0.4.2
Parsing documentation for thor-0.19.1
Installing ri documentation for thor-0.19.1
Parsing documentation for acm-0.4.2
Installing ri documentation for acm-0.4.2
Done installing documentation for thor, acm after 2 seconds
2 gems installed

OK。

参考ページ

 cf. Ruby distros, RubyGems, and SSL—Oy vey!

新しいPCが届いた

HP の EliteBook Folio G1 ってやつ。1週間ほど前の夜中、薄くて軽くて頑丈、っていう惹句についうっかりとポチッたもの。確かに12.5型にしては軽い。
OS は Ubuntu じゃなくてプリインストールの Windows 10 Home をそのまま使うことにした。MS-Office を使いたいからそのほうがいいんだよね。
朝からセットアップを始めて、一通り終わったところ。rubygems がエラーをはいてちょっとハマったけど、他は特に問題なし。ホスト名は sofa にした。
以下、インストールしたアプリケーション。

  • Microsoft Office 2013
  • Firefox
  • Dropbox
  • Ruby
  • Python
  • Node.js
  • Gauche
  • Haskell
  • Perl
  • Git
  • 秀丸エディタ

とりあえずこんなところか。rubygems の問題の解決方法はエントリーを分けて書く。

辞書の値によってキーを削除する

例えば、こんな辞書があったとする。

>>> dic = {'Andy': 27, 'Bill': 32, 'Charlie': 24}

この辞書から、値が 30 を超えるキー(つまり ‘Bill’)を削除したい。こうすると:

>>> for k in dic:
...     if dic[k] > 30:
...         del(dic[k])
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

エラーになる(イテレーションの最中に辞書のサイズが変わってるって怒られる)。
でもキーは削除されてる。

>>> dic
{'Charlie': 24, 'Andy': 27}

なんでだかは解らない。
エラーが出ないようにするには items() メソッドを使えばいい。今度は値が 25 を超えるキーを削除してみる。

>>> for k, v in dic.items():
...     if v > 25:
...         del(dic[k])
...
>>> dic
{'Charlie': 24}

無事に消えた。

Pythonの軽量Webフレームワークbottleを使ってみる(2)

テンプレートについて補足。
昨日のスクリプトでは、template 関数の引数に直接テンプレートを文字列として渡していた。短いテンプレートならそれでもいいけど、引数として文字列で渡すには長すぎるのが普通だ。そこで、テンプレートそのものじゃなく、名前で指定できるようになっている。
テンプレートは、スクリプトと同じディレクトリの下に views という名前でディレクトリを作って、その中に入れておく。そうすると bottle がテンプレートを見つけて読み込んでくれる。
次のスクリプトでは info という名前のテンプレートを使っている。

# encoding: utf-8

from bottle import route, request, template, run

@route('/')
def index():
    return "<a href="\&quot;./info\&quot;">Link</a>"

@route('/info')
def info():
    ip = request.environ.get('REMOTE_ADDR')
    referer = request.headers.get('Referer') or ''
    return template("info", ip=ip, referer=referer)

run(host='localhost', port='8080', debug=True, reloader=True)

で、テンプレートのほうはこう。

<h1>Remote information</h1>
IP address: {{ip}}

Referer: {{referer}}

Pythonの軽量Webフレームワークbottleを使ってみる

インストール

pip でインストール。

takatoh@apostrophe $ pip install bottle
Collecting bottle
/usr/local/lib/python2.7/dist-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
  Downloading bottle-0.12.10-py2-none-any.whl (88kB)
    100% |████████████████████████████████| 90kB 3.6MB/s 
Installing collected packages: bottle
Exception:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/pip/basecommand.py", line 211, in main
    status = self.run(options, args)
  File "/usr/local/lib/python2.7/dist-packages/pip/commands/install.py", line 311, in run
    root=options.root_path,
  File "/usr/local/lib/python2.7/dist-packages/pip/req/req_set.py", line 646, in install
    **kwargs
  File "/usr/local/lib/python2.7/dist-packages/pip/req/req_install.py", line 803, in install
    self.move_wheel_files(self.source_dir, root=root)
  File "/usr/local/lib/python2.7/dist-packages/pip/req/req_install.py", line 998, in move_wheel_files
    isolated=self.isolated,
  File "/usr/local/lib/python2.7/dist-packages/pip/wheel.py", line 339, in move_wheel_files
    clobber(source, lib_dir, True)
  File "/usr/local/lib/python2.7/dist-packages/pip/wheel.py", line 317, in clobber
    shutil.copyfile(srcfile, destfile)
  File "/usr/lib/python2.7/shutil.py", line 83, in copyfile
    with open(dst, 'wb') as fdst:
IOError: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/bottle.py'
You are using pip version 7.1.2, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

って、えー、なんかいきなりエラーが出るじゃん。pip をアップグレードしろ、といってるみたいなので、そうしてみる。

takatoh@apostrophe $ pip install --upgrade pip
/usr/local/lib/python2.7/dist-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
Collecting pip
  Downloading pip-9.0.1-py2.py3-none-any.whl (1.3MB)
    100% |████████████████████████████████| 1.3MB 451kB/s 
Installing collected packages: pip
  Found existing installation: pip 7.1.2
    Uninstalling pip-7.1.2:
Exception:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/pip/basecommand.py", line 211, in main
    status = self.run(options, args)
  File "/usr/local/lib/python2.7/dist-packages/pip/commands/install.py", line 311, in run
    root=options.root_path,
  File "/usr/local/lib/python2.7/dist-packages/pip/req/req_set.py", line 640, in install
    requirement.uninstall(auto_confirm=True)
  File "/usr/local/lib/python2.7/dist-packages/pip/req/req_install.py", line 716, in uninstall
    paths_to_remove.remove(auto_confirm)
  File "/usr/local/lib/python2.7/dist-packages/pip/req/req_uninstall.py", line 125, in remove
    renames(path, new_path)
  File "/usr/local/lib/python2.7/dist-packages/pip/utils/__init__.py", line 315, in renames
    shutil.move(old, new)
  File "/usr/lib/python2.7/shutil.py", line 303, in move
    os.unlink(src)
OSError: [Errno 13] Permission denied: '/usr/bin/pip'
You are using pip version 7.1.2, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

ダメじゃん。同じようなエラーが出る。
ググってみると↓このページを見つけた。

 cf. bottle超初歩入門 – Qiita

なんか、pip でインストールしようとするのが間違いみたいなことが書いてある。bottle はファイル1つでできているので、それをダウンロードして、スクリプトと同じディレクトリに置くのが簡単らしい。

https://raw.githubusercontent.com/bottlepy/bottle/master/bottle.py

takatoh@apostrophe $ wget https://raw.githubusercontent.com/bottlepy/bottle/master/bottle.py

これで bottle.py がダウンロードできた。なんか釈然としないけど今のところはよしとする。

Hello, world!

まず初めは、定石通り Hello, world。

from bottle import route, run

@route('/hello')
def hello():
    return "Hello, world!"

run(host='localhost', port='8080', debug=True)

bottle から routerun をインポート。route デコレータでルーティングを行い、run 関数でサーバを走らせる、ってことのようだ。
これで http://localhost:8080/hello にブラウザでアクセスすると、ちゃんと「Hello, world!」と表示された。

パス中にパラメータ

URL のパス中にパラメータを入れることができる。下の < > で囲んであるところがそれ。

from bottle import route, run

@route('/hello/')
def hello_with_name(name):
    return "Hello, " + name + "!"

run(host='localhost', port='8080', debug=True)

例えば、http://localhost:8080/hello/Andy にアクセスすると「Hello, Andy!」と返ってくる。

リダイレクト

redirect 関数を使う。

from bottle import route, redirect, run

@route('/')
def index():
    redirect('/hello')

@route('/hello')
def hello():
    return "Hello, world!"

run(host='localhost', port='8080', debug=True, reloader=True)

ついでに、run 関数の引数に reloader=True を指定した。これによって、サーバを起動したままでもファイルを書き換えると自動的にリロードしてくれるようになる。

Not found

abort 関数。ステータスコードとメッセージを指定できる。

from bottle import route, redirect, abort, run

@route('/')
def index():
    redirect('/hello')

@route('/hello')
def hello():
    abort(404, "Not Found.")

run(host='localhost', port='8080', debug=True, reloader=True)

この例では、http://localhost:8080/ にアクセスすると /hello にリダイレクトされて、Not found になる。

リファラと接続元IPアドレス

request オブジェクトからリクエストの情報が取れる。リファラは request.headers.get('Referer')

# encoding: utf-8

from bottle import route, request, run

@route('/')
def index():
    return "<a href="\&quot;./ref\&quot;">Link</a>"

@route('/ref')
def ref():
    referer = request.headers.get('Referer')
    return referer

run(host='localhost', port='8080', debug=True, reloader=True)

一方、IPアドレスは request.environ.get('REMOTE_ADDR')

# encoding: utf-8

from bottle import route, request, run

@route('/my_ip')
def my_ip():
    ip = request.environ.get('REMOTE_ADDR')
    return ip

run(host='localhost', port='8080', debug=True, reloader=True)

テンプレート

bottle から template をインポートすることで、簡易なテンプレートエンジンが使えるようになる。

# encoding: utf-8

from bottle import route, request, template, run

@route('/')
def index():
    return "<a href="\&quot;./ref\&quot;">Link</a>"

@route('/ref')
def ref():
    ip = request.environ.get('REMOTE_ADDR')
    referer = request.headers.get('Referer') or ''
    return template("Your IP is {{ip}}, referer is {{referer}}.", ip=ip, referer=referer)

run(host='localhost', port='8080', debug=True, reloader=True)

静的ファイル

static_file 関数を使う。

# encoding: utf-8

from bottle import route, static_file, run

@route('/')
def static(file_path):
    return static_file(file_path, root='./storage', download=True)

run(host='localhost', port='8080', degug=True, reloader=True)

引数に download=True を指定すると、ブラウザで開くのではなくダウンロードするようになる。

ふう、今日のところはここまで。

[追記]

Windows 10 では、pip で普通にインストールできた。なんでだ?

[追記2]

sudo をつけたら、pip のアップグレードも bottle のインストールもうまくいった。

リストから要素を削除する

Haskell での話。リストから特定の要素を削除したくて、ググってみたけどそれらしい関数が見当たらない。需要がないはずがないと思いながらしばし考えて、filter を使えばいいということに思い当たった。
というわけでメモ。

Prelude> let delete x xs = filter (/= x) xs
Prelude> delete 3 [1..9]
[1,2,4,5,6,7,8,9]

[追記]

Data.List モジュールに delete という関数があった。探し方が悪いな。
でも、この関数は上のと違って、最初にマッチした要素しか削除してくれない。

Prelude> import Data.List
Prelude Data.List> delete 3 [1,3,2,3]
[1,2,3]

文字列を長さ1の文字列のリストに分割したい(Haskell編)

以前、Scheme では書いた。

 cf. 文字列を長さ1の文字列のリストに分割したい – blog.PanicBlanket.com

今日は Haskell でやってみよう。

Prelude> map (\ c -> [c]) "abcdefg"
["a","b","c","d","e","f","g"]

というわけで、とりあえず出来たわけだけどなんだか冗長できれいじゃない。
こんなふうじゃダメなんだろうか。

Prelude> map ([]) "abcdefg"

<interactive>:3:6:
    Couldn't match expected type `Char -> b0' with actual type `[a0]'
    In the first argument of `map', namely `([])'
    In the expression: map ([]) "abcdefg"
    In an equation for `it': it = map ([]) "abcdefg"

ダメだった。
いや、空リストに cons すればいいのか。

Prelude> map (:[]) "abcdefg"
["a","b","c","d","e","f","g"]

出来た。

Schemeで数値を文字列に変換するには

number->string 手続きが使える。このあいだ Look and Say 数列を作った時にはググっても何故か見つからなくて、先に見つかった write-to-string を使ったんだけど、number->string のほうが名前も自然だし短い。

gosh> number->string
#<subr number->string>
gosh> (number->string 123)
"123"
gosh> (number->string 3.14)
"3.14"

ちなみに、文字列を数値に変換するには string->number

gosh> string->number
#<subr string->number>
gosh> (string->number "3.14")
3.14