VSCodeではNotebook形式のファイルも扱えて色々便利なのですが、デフォルト設定では各セルを実行したときに勝手にスクロールされてしまい、 直前にどこを実行したのかわからなくなることがあり、無効にする方法を探してみました。 以下の設定を追加で無効にできました。
settings.json
に以下を追加する。
{ // これを追加する "notebook.scrolling.revealNextCellOnExecute": "none", }
ソースはこちら github.com
VSCodeではNotebook形式のファイルも扱えて色々便利なのですが、デフォルト設定では各セルを実行したときに勝手にスクロールされてしまい、 直前にどこを実行したのかわからなくなることがあり、無効にする方法を探してみました。 以下の設定を追加で無効にできました。
settings.json
に以下を追加する。
{ // これを追加する "notebook.scrolling.revealNextCellOnExecute": "none", }
ソースはこちら github.com
バッファからバイト文字をスライスに移した際に、string
関数を使って文字列変換しても、スライスの余りの部分(0初期化した値)も文字変換されてしまうため、それを取り除く必要がある。
buf := make([]byte, 128) // \x00で初期化される b, err := io.Read(buf) if err != nil { panic(err) } string(b) == "hello" // false strings.TrimRight(b, "\x00") == "hello" // true
少し前にJuliaのHTTP.jlを使ってbitflyerからリアルタイムに価格データを取得するコードを書いたので、備忘録として書き残します。 ※Javascriptなどのサンプルコードは公式ページ(https://bf-lightning-api.readme.io/docs/endpoint-json-rpc)にて提供されています。
HTTP.jlがWebSocketsモジュールを提供しているので、これを使います。
using HTTP using HTTP.WebSockets using URIs using JSON3 # wss://ws.lightstream.bitflyer.com/json-rpc url = URI(scheme="wss", host="ws.lightstream.bitflyer.com", path="/json-rpc") |> string # 取得する仮想通貨のコード product_code = "BTC_JPY" WebSockets.open(url) do ws try # JSONRPCのプロトコルに従って、 # コネクション時にsubscribeをリクエストする msg = Dict( "version" => "2.0", "method" => "subscribe", "params" => Dict( "channel" => "lightning_ticker_$(product_code)", ), ) @debug msg send(ws, JSON3.write(msg)) # リクエスト @info "open" for msg in ws # レスポンスデータ @show msg end catch e error(e) finally close(ws) @info "closed" end end
接続に成功すると、次のようなJSONが逐次送られてきます。
"{\"jsonrpc\":\"2.0\",\"method\":\"channelMessage\",\"params\":{\"channel\":\"lightning_ticker_BTC_JPY\",\"message\":{\"product_code\":\"BTC_JPY\",\"state\":\"RUNNING\",\"timestamp\":\"2023-08-17T07:25:01.3680432Z\",\"tick_id\":59827584,\"best_bid\":4197736.0,\"best_ask\":4198155.0,\"best_bid_size\":0.01,\"best_ask_size\":0.10,\"total_bid_depth\":390.953720690000,\"total_ask_depth\":338.76347988,\"market_bid_size\":0.0,\"market_ask_size\":0.0,\"ltp\":4198155.0,\"volume\":1211.068567520000,\"volume_by_product\":1211.068567520000,\"preopen_end\":null,\"circuit_break_end\":null}}}"
あとは必要に応じ型を用意して、JSON3.read
でjson文字列ををparseすればOK
juliaではBase.Threads
モジュールが標準で提供されていて、簡単にマルチスレッド処理を実装することができます。
ここでは、次の2つのマクロを紹介します。
@threads
@spawn
@threads
for文を渡すと、マルチスレッド化してくれます。
using Base.Threads n = 1000000 x = randn(n) y = randn(n) z = zeros(n) # マルチスレッドで実行 @threads for i in 1:length(z) z[i] = x[i] * y[i] end
マルチスレッドを利用するには実行時オプション-threads n
で設定する必要があります。
実行可能なスレッド数を確認するにはThreads.nthreds()
メソッドを呼び出すとスレッド数が返ります。
@spawn
マクロに続く式を実行するタスク*1を生成して、空きスレッドで実行します。
for i in 1:10 task = @spawn some_work() end
@spawnを付与した関数や式はtask(Task型)を返します。@spawn
で生成したタスクは、非同期で実行されます。
タスク化したものが返り値を返す場合はfetch
関数が便利です。fetch
を使うと、タスクが完了するまで待機させることができます。
ただ、IOをブロックしてしまうので、ブロックさせたくない場合はBase.@async
でそれ自体を別タスクで非同期実行する必要があります。
複数の音声データをダウンロードする処理を、マルチスレッドかつマルチタスクで実行するコードを書いてみました。
# download.jl using HTTP using Base.Threads # 指定のURLからファイルをダウンロードする function download(url, timeout=180) filename = basename(url) try @info "threadid=$(threadid()): download start $(filename)" res = HTTP.get(url, readtimeout=timeout) @info "threadid=$(threadid()): download finished $(filename)" return (filename=filename, filecontent=res.body) catch e @error e return end end function main() urls = [ "https://archive.org/download/ThePianoMusicOfMauriceRavel/01PavanePourUneInfanteDfuntePourPianoMr19.mp3", "https://archive.org/download/ThePianoMusicOfMauriceRavel/02JeuxDeauPourPianoMr30.mp3", "https://archive.org/download/ThePianoMusicOfMauriceRavel/03SonatinePourPianoMr40-Modr.mp3", "https://archive.org/download/ThePianoMusicOfMauriceRavel/04MouvementDeMenuet.mp3", "https://archive.org/download/ThePianoMusicOfMauriceRavel/05Anim.mp3", ] tasks = Task[] for url in urls # 逐次ダウンロードを開始する task = @spawn download(url) push!(tasks, task) end @sync for task in tasks @async begin result = fetch(task) result === nothing && ErrorException("download is failed.") path = joinpath(dirname(@__FILE__), "tmp", result.filename) try open(path, "w") do file write(file, result.filecontent) end catch e error(e) end end end end main()
@__FILE__
は実行ファイルパスを取得するマクロbegin ~ end
は複数の式を一つのブロック化するときに役立ちます。スコープは作らないので、ローカルスコープが欲しい場合はlet ~ end
を使います。@async ~
はbegin~end
ブロックを非同期実行します。@sync
で全体をラップすることで、@async
で非同期実行された式がすべて完了するまでfor~end
のスコープを抜けるのを待つことができます。上記コードをスレッド数を指定して上記スクリプトを実行するには
julia --threads 4 download.jl
と実行すれば、マルチスレッド&マルチタスクでファイルダウンロードが実行されます.
この例は、非同期実行すれば十分な内容ではあると思いますが、@spawn
を使うと簡単に非同期かつマルチスレッドな実行が実現できるということがわかります。
JuliaでWebAPIサーバの作り方を勉強しているので、備忘録も兼ねて書き連ねていきます。
HTTP.jlを使うと簡単にWebサーバを建てることができます。 Genie.jlというJulia製のWebフレームワークがありますが、今回は使用しません。 なお、Genie.jlもHTTP.jlをベースに書かれているそうです。
まずは、インデックスルート"/"に対する簡単なHTTPサーバ構成を示します。
using HTTP # ルータを用意 const ROUTER = HTTP.Router() # ハンドラーを定義 function index(req::HTTP.Request) @show req return HTTP.Response(200, ["Content-Type" => "text/plain"], "hello, world") end # ルータにハンドラを登録する HTTP.register!(ROUTER, "GET", "/", index) # ローカルアドレス address = Sockets.localhost # ポート番号 port = 8080 # HTTPサーバ起動 HTTP.serve(ROUTER, address, port)
http://localhost:8080/
にアクセスするとhello, world
が返ってきます。ctrl + c
で停止できます。
上記の例ではインデックスパス"/"に対してGETメソッドを受けたときのハンドラとしてindex関数を登録しています。
ハンドラ関数には引数HTTP.Request
型を取る関数で、戻り値にはHTTP.Response
型を返します。
こんな感じで、HTTP.jlを使うと簡単にWebサーバを建てることができます。
続いてこれをリクエストボディにJSONを返すAPIに書き換えていきます。
using JSON3 # indexを書き換える function index(req::HTTP.Request) body = Dict{Symbol,String}(:message => "hello, world") # ヘッダーにJSONを返すことを示す headers = ["Content-Type" => "application/json"] return HTTP.Response(200, headers, JSON3.write(body)) end # 再登録 HTTP.register!(ROUTER, "GET", "/", index) # 再起動 HTTP.serve(ROUTER, address, port)
これで、http://localhost:8080/
にアクセスすると{"message": "hello, world"}
というJSONを返すようになります。
まず、JSON3パッケージを使うとJuliaのオブジェクトをJSONにシリアライズすることができます。
# Julia -> JSON文字列 json = JSON3.write(Dict(:message => "message", :data => "data")) # JSON文字列からJuliaオブジェクト JSON3.read(json) println(json.message) println(json.data)
ここで、JSON3.read
が返すオブジェクトは汎用的なオブジェクトで、Juliaのユーザ定義型(構造体)にデシリアライズするためにはStructTypes.jl
というパッケージを使うと実現できます。※Juliaの構造体に明示的にparseすることで、エラーチェックなどもついでに行うことができます。
using StructTypes # ユーザ型を定義 struct RequestBody name::String email::String end # StructTypeに型を登録する StructTypes.StructType(::Type{RequestBody}) = StructTypes.Struct() body = """{ "name": "taro", "email": "taro@example.com" }""" obj = JSON3.read(body, RequestBody) println(obj) # RequestBody("taro", "taro@example.com")
jsベースで記述します。
.: 任意の1文字が1個
+: 直前の文字が1回以上繰り返す
?: 直前の文字が0個または1個
*: 直前の文字が0回以上繰り返す
^: 先頭が次の文字に一致する
$: 末尾が前の文字に一致する
(): グループ化・複数の条件などをひとまとめにすることができる
注意点 - 数字指定などにおいて最後に$を指定しないと先頭が一致する限りtrueとなってしまう
// 数字のチェック // 半角数字 [0-9] // 0パディングを許さない整数 /^([1-9][0-9])$/ // 0または3桁以下の整数 /^([1-9][0-9]{0,2}|0)$/ // 任意の実数(整数部一桁時のみ0を許す, 小数部省略可) /^([1-9]\d*|0)(\.d*)?$/ // 任意の桁数 /^([1-9]\d{0,3}|0)(\.d{0,1})?$/
Javaのコレクション周りの備忘録
目次
List
動的配列
// Listの初期化 List<Integer> list = new ArrayList<>(); // Java 7以降 // 値の追加 list.add(1); list.add(2); // indexに対応する要素の取得 list.get(0); list.remove(1); // 1番目の要素を削除
Listの初期化パターン
// 固定リストとして初期化 List<String> list = Arrays.asList("Apple", "Banana", "Orange"); // 可変リストとして初期化 List<String> list = new ArrayList<>(Arrays.asList("Apple", "Banana", "Orange")); // staticイニシャライザを用いる List<String> list = new ArrayList<>() { { add("Apple"); add("Banana"); } }
Map
Map<String, Integer> map = new HashMap<>(); map.put("banana", 100); map.put("apple", 200); map.put("orange", 300); Set<String> set = new HashSet<>(); set.add("fruits"); set.add("rice"); set.add("meet"); set.add("vegetable");
特徴
map
List<Integer> list = Arrays.asList(1,2,3,4,5); List<Integer> expon = list.map(i -> Math.exp(i)).collect(Collectors.toList());
filter
List<Integer> list = Arrays.asList(1,2,3,4,5); List<Integer> oddNumbers = list.stream().filter(i -> i % 2 != 0).collect(Collectors.toList());
グループ化して処理する
class Food { private String name; private String category; private Integer price; public Food(String name, String category, Integer price) { this.name = name; this.category = category; this.price = price; } public String getCategory() { return this.category; } public Integer getPrice() { return this.price; } } List<Food> foods = new ArrayList<>(); foods.add(new Food("りんご", "フルーツ", 100)); foods.add(new Food("ばなな", "フルーツ", 50)); foods.add(new Food("白米", "炭水化物", 50)); foods.add(new Food("餅", "炭水化物", 200)); foods.add(new Food("鱈", "魚", 300)); foods.add(new Food("鮭", "魚", 300)); foods.add(new Food("牛肉", "肉", 400)); foods.add(new Food("豚肉", "肉", 300)); // カテゴリごとに集計する Map<String, Integer> aggregated = foods.stream().collect( Collectors.groupingBy( Food::getCategory, Collectors.summingInt(Food::getPrice) ) ); aggregated.forEach((category, subtotal) -> { System.out.println(category + " => " + subtotal); // フルーツ => 150... });