19日に更新してた

アフィリエイトはないよ

golang 2重? 2次元? 配列とかスライスとか

配列だと

package main
import "fmt"
func main(){
    a:=[3][]int{}
    a[0]=append(a[0],1)
    fmt.Println(a)
}
>>[[1] [] []]

という感じで、使える部分を零詰めして対応するなら、

package main
import "fmt"
func main(){
    l:=[3][3]int{}
    l[0][1]=1
    fmt.Println(l)
}
>>[[0 1 0] [0 0 0] [0 0 0]]

こんな感じで、スライスだと

package main
import "fmt"
func main(){
    s:=[][]int{}
    s=append(s,[]int{1})
    s[0]=append(s[0],2)
    fmt.Println(s)
}
>> [[1 2]]

こんな感じ。

golang の exec.Command で ffmpeg をやっと動かせた

golang をいじってみようかなと思ってはじめてみたら、とりあえず検索するとやりたいことが割と出てくるのでとても書きやすい。rust みたいに型と所有権で足踏みしてエラーメッセージで四苦八苦しなくていいし、できるようになった感がしてサンデープログラマーの自分にはいい感じ。

こんなに書きやすいなら python で書いた" ffmpeg を使ったダウンローダー"を移植してみようとやってみたところ、agouti を使えるようになったらサクサク進んだのですが最後の最後に exec.Command で ffmpeg をうまく通せない。

数日色々と弄り回してみたら、やっと原因がわかったのでご報告。

ffmpeg でストリーミングをダウンロードしようとしたら、ヘッダとか m3u ファイルはダブルクォーテーション*1をつけて

ffmpeg -headers "X-Radiko-AuthToken:aaaaaaaaa" -i "https://radiko.jp/~"

みたいな感じで書いていたのでそのまま入力して、

ffmpeg_command := "ffmpeg -headers \"X-Radiko-AuthToken:aaaaaaaaa\" -i \"https://radiko.jp/~\" ./a.m4a"
cmdArray := strings.Split(ffmpeg_command, " ")

cmd := exec.Command(cmdArray[0], cmdArray[1:]...)

みたいな感じから cmd.Run() してたのですが、これだとエラーが出るのでいかがしたものかと色々やってみたら、ダブルクォーテーションは入れないのが正解でした。クォーテーション*2もいらないみたい。

ついでに、例には挙げていないけれど半角スペースで分割すると、metadata の中に半角スペースを使った時に metadata の中で分割されてしまうのでカンマで区切って

ffmpeg_command := "ffmpeg,-headers,X-Radiko-AuthToken:aaaaaaaaa,-i,https://radiko.jp/,./a.m4a"
cmdArray := strings.Split(ffmpeg_command, ",")

cmd := exec.Command(cmdArray[0], cmdArray[1:]...)

とやってやったら、半角スペースを入れた metadata も通ったのでご査収ください。

ちなみに作ったものは async も go routine も使ってない*3ベタなものなのですが、ここ数回のダウンロードの所要時間を平均すると python のときと比べて所要時間が10秒ほど短くなっています。webpage の内容引っ張ってからの json の参照*4とかファイル周り以外、ほとんど ffmpeg のダウンロードだろうに差が出るものなんだなと感心しています。

*1:"

*2:'

*3:そもそも使い方がわかっていないので使えない

*4:json を struct に変換してくれるサイトには感動した。メッチャ楽。

golang で agouti をなんとか入れた

「新しいプログラミング言語を身につけたい」と思いつつ、ちょっと手を付けては放り出してます。どうせプログラマーワナビーだから気分良く書けるかと心が折れないことが大事とばかりにとっかえひっかえしても、公式のオンラインチュートリアルだと金掛からないからいくらでも手を出せます。
python も初心者なのにどうなんだ?」という想いもございますが仕事じゃないから気楽なものです。

とりあえず、欲しいプログラムが時間をかければなんとか書けるくらいだとその恩恵を実感することはあまりないのでしょうけれど「はやい python」が欲しいので、難易度があまり高くないとどこかに出ていた golang に手を出してみました。*1

とりあえず、前にやったことがあることを試してみるのが一番楽なので、以前 rust でやってみた a^5+b^5+c^5+d^5=e^5 をベタでやったら138秒、array に5乗の計算結果を入れてループ組んで index で array にアクセスして足し算判定してみたら1秒くらいでしたし、思ったより簡単に書けるからいいなと。*2
あんまり苦労することなくいろいろとサクサク進むのでいいなぁと思っていたのですが、Agouti を入れようとしたら

go install github.com/sclevine/agouti@latest

>>package github.com/sclevine/agouti is not a main package

となりまして、go run してみたら動きませんでした。*3

go install golang.org/x/tools/cmd/stringer@latest

>>go: downloading golang.org/x/tools v0.1.12
>>go: downloading golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f       
>>go: downloading golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4

だと問題ないみたいだから、環境設定の問題ではないみたい。

ネットでの解説は go get ばかりで試しにやってみたら go install を使えとくるし、仕方がないので go get の使い方を調べてみたらうまく行ったのでご報告。

go env -w GO111MODULE=off 

go get -v github.com/sclevine/agouti

でいいらしい。その後、GO111MODULE を再度 on にすると go run できなくなるので、agouti を使う場合は off でいかないといけないみたい。

agouti 自体は pythonselenium と変わりなく使えるのでとてもなじみがいいです。

*1:go version go1.19.1 windows/amd64

*2:両方とも go run で動かしています

*3:@v3.0.0も@v2.0もあきませんでした

python で if elif else を複数書いて処理するのが面倒くさい

組み合わせを検討して関数を実行していくときに、if 文の条件を複数書くのが面倒くさい。

単純にフラグが立ったものを実行するのにいい方法はないのかと文法を色々見てみたけれど、書き方が好みのものがありませんでした。

仕方がないので、あんまりいい感じではないのでしょうが書いてみました。

def a():
    print("a",end="")

def b():
    print("b",end="")

def c():
    print("c",end="")

k=["a()","b()","c()"]

for i in range(1<<len(k)):
    for s,t in zip(format(i,f"0{len(k)}b"),k):
        if s=="1":
            exec(t)
    print("")

こんな感じの jupyter notebook で探すときだけに使う書き捨て用です。

フラグを立てることのみを考えて、

format(i,f"0{len(k)}b")
# もしくは f"{i:0{len(k)}b}"

にすることで、i が len(k) 桁 = 準備した関数の数のフラグに対応した桁数 の文字列の2進数表記になるため、特定の範囲で数指定の対応*1がスライス切って計算するだけなので、非常に楽になります。

範囲指定だけですが、下に書いてみました。

最初の範囲、次の範囲が v0 , v1 、最初の範囲で lim0 個、次の範囲で lim1 個以上当てはめて計算したいという想定で、

v0=3
v1=5
lim0=1
lim1=3

total=v0+v1

for f in range(1<<v1,1<<total):
    f2=[int(b) for b in format(f,f'0{total}b')]
    if sum(f2[:v0])>=lim0 and sum(f2[-v1:])>=lim1:
        print(f,f2)

こんな感じです。

*1:全部で15個の関数のうち、最初の8個から4個以上、残りから2個以上の組み合わせが欲しい、なんてケース

pandas.read_csv の ssl エラー

こちらのサイトでビットコインのデータダウンロードが紹介されているのですが、wget でダウンロードして pandas に読み込んでいます。

aifx.tech

直接 pandas.read_csv で読み込めそうなものですが、なにかがあるのでしょうと jupyter notebook 上で試してみると、

import pandas as pd

df=pd.read_csv("https://api.bitcoincharts.com/v1/csv/bitstampUSD.csv.gz")

# URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1131)>

ssl 関係のエラーが出ます。これを検索してみると stackoverflow のこちらに同じようなのが出ています。

stackoverflow.com

同じようにやってみましょう。

import ssl
import pandas as pd

ssl._create_default_https_context = ssl._create_unverified_context
df=pd.read_csv("https://api.bitcoincharts.com/v1/csv/bitstampUSD.csv.gz")

無事ダウンロードできました。ファイルサイズが500M 位あるので時間かかります。

python3.8 で int("1_234") は 1234 だが、int("1,234") はエラー

数字を表記するとき何桁かでアンダーバーを入れると見やすいのですが、format 文で可能な3桁だと桁計算がはかどらないので4桁で入れようとするとテキスト処理することになります。そうなると色々と面倒そうなので、アンダーバーを入れたテキストそのまま数字の戻せるのかとやってみたら、できるんですね。*1

int("1_234")    #1234
float("4_5_6.7_8_9")    #456.789

カンマだと Value Error がでます。

int("1,234")

# ValueError: invalid literal for int() with base 10: '1,234'

アンダーバーもカンマも format 文だと表記されるのですが、読み込みはアンダーバーだけのようです。

2進数、8進数、16進数などですと、int への直入力ではアンダーバーは問題ないみたいです。テキスト入力はオプションが必要。

int(0b_1_0_0)    #4
int(0o_1_1)    #9
int(0x_f_f)    #255

int("0b_1_0_0",2)    #4
int("0o_1_1",8)    #9
int("0x_f_f",16)    #255

これも知らなかったのですけれど、

bin(3)
#'0b10'
oct(9)
#'0o11'
hex(17)
#'0x11'

type(bin(3))
#str
type(oct(9))
#str
type(hex(17))
#str

出力を見ればわかるのですが、bin、oct、hex は文字列で出力されるのですね。

ちなみにバージョン

import sys
sys.version

>>3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 05:59:45) [MSC v.1929 64 bit (AMD64)]

*1:float の場合はドットの前後にアンダーバーを入れるとエラーになるのですが、それ以外なら変換できるようです。

a, b, c = range(3) ってエラーにならないのをはじめて知った。python3.8

python を使ってみよう」と思いついて、勢いでネットで検索しまくって試行錯誤で python3 を学習してきました。

なので、最近になって一応解説本を読んではいるのですが「わかってる、わかってる」で飛ばしまくっているので、ごくごく基本的なことを知らなかったりすることがよくあります。

文法*1のネットでの解説などを眺めているとあったのが

a, b = map(int,"1 2".split())

これ、エラーでないんだと最近はじめて知りました。

イテラブルなものは直接変数に置き換えられるってこと?

変数に置き換えは

a, b = list(map(int,"1 2".split()))
#or
a, b = [*map(int,"1 2".split())]
#or
a, b = [int(i) for i in "1 2".split()]

みたいにリストでないといけないと思っていたのでした。

となると、

a, *b = map(int,"1 2 3".split())

が成り立つのはわかります。で、

a, b, c = range(3)
d, *e = range(3)

でもいけるのかと試してみたら、両方ともいけますね。*2

当たり前のことなんでしょうけれど、ちょっと発見した気分で嬉しい。

import sys
sys.version

#3.8.13 | packaged by conda-forge | (default, Mar 25 2022, 05:59:45) [MSC v.1929 64 bit (AMD64)]

*1:競プロ?

*2:すぐに出てくるリストでないイテラブルがこれだけでした。