Rust でう~ん?と思うところのメモ

expect というメソッド名

Perl の die() に似ている関数で、エラーメッセージを出してプログラムを強制終了(Rustでは panic!()) する Result 型にあるメソッド。

APIリファレンス(↗)をみると、 引数に渡すメッセージがどうあるべきか(Common Message Style ↗)まで 指定されているんだけど、The Book のサンプルコード見てても、それに従ってないような気がする。

expect という関数名も、そのメッセージスタイルに従うことを前提として関数名を決めたみたいなんだけど、 この関数名のせいで、コードが読みにくくなっている気がする。いわゆる、不適切な命名*1にみえてしまう。

まだ Rust になじんでないだけかもしれないけど。

Common Message Style も一応読んで見たけど、まだピンとこない。

  1. エラーが起こる
  2. どんなエラーかをプリントする。

というのは悪いスタイルらしく

  1. エラーが起こる
  2. 何を期待していたか(どうしていればエラーを回避できたか)をプリントする。

というルールがよいとしている。 でも、まず、エラーが起こったことを教えてほしいな。どうすればエラーを回避できたかはその後な気がする。 まあ、panic! するので、何らかのエラーが起こったことは分かってるはずだから、回避方法だけ書けばよい! という意図のようだけど。 (あと、エラーが出たときにプログラマがコードを見て、何を期待していたか書いてあるとわかりやすいという意図もあるらしい)

うーん。慣れれば使いやすいのだろうか。まだ分かってない。 まあ、panic! の出力をそのままユーザーに見せること自体、製品レベルのコードではあり得ないだろうから、そこまで気にしなくてよいのかもしれないけど。

ヒープ多用

ヒープの利用を前提としているところが多いような印象。 まあ、String, Vector, Box なんかは仕方ないにしても、 動的な多相性を dyn を使って実現するときも、ヒープメモリの利用が欠かせないように読めたけど、どうだっけ?

あと、すべてのエラーを表すのにも動的な多相性(dyn)を利用するので、エラー処理を統一したいとか思ってもヒープ必要になりそうな?

日本の組み込み開発では、ヒープはなるべく使わないというところもあるので、そのへん普及の足かせになりそう。

まあ、起動時にヒープを取得してずっと使い続けるような運用なら問題はなさそうだけど。

※ヒープの獲得と解放を繰り返すときの問題点

  • メモリが断片化して、いつか必要な量のメモリが確保できなくなる恐れあり。
  • ヒープ獲得時にはロックが必要なので、リアルタイム性確保のためにその遅延が許されない場合もある。
  • 組み込みでヒープ使うときはアロケートシステムを独自実装することになるだろうから、難易度が高いかも。

組み込み開発でRustを使うなら、ヒープも使う方向に舵を切るべきかも。

Rustを使ったLinuxカーネルドライバの開発では、ヒープも使っているぽい映像(↗)あった。 もともとLinuxはヒープ使うのを、ためらったりしてないのかな。

Rust でグローバル変数にアクセスするときは常に Mutex 必要(unsafe という選択肢もあるけど)

組み込みシステムでグローバル変数排他制御は、

  • 同一タイミングに同じ領域に同時アクセスしないことによる排他

という、運用上の排他が行われていることもある。タイミング管理を人間が行う必要があるけど、 OSが提供するロック機能を使う必要が無いので、遅延がないのが利点。

まあ、ロックする時間はちょびっとなので、これはRustによる排他制御を受け入れた方が 不具合は減りそうだから、そのほうがいいかな。

ただし、Mutex とか Arc とか使うときも、どうやらヒープを使うみたいなのでそっちの方がネックかな。

LinuxカーネルドライバをRustで開発するChatGPTが生成したらしき例Gistみると、 Linux との界面は unsafe にしておいて、C と Rust を切り分けてるみたい。

ドライバ用のデータは、C側に持たせておいて、unsafe な API 呼ぶときに渡してもらうようにしているらしい。 これなら、自分でグローバル変数持たずにすむから Rust で Mutex とる必要もないか。

これを、Rust単体でやろうとすると、結局グローバル変数にアクセスするところはすべて unsafe にしておいて 残りは unsafe でないコードでやる感じかな。(レジスタアクセスは unsafe になるだろうけど)

一部の日本語訳にう~ん?

"トレイト境界" → トレイト制約 のほうが好き

trait bound の日本語訳。

トレイトに対して制約を加える機能なので、トレイト制約がよいと思う。
オライリーの『プログラミングRust 第2版』でもトレイト制約を採用しているし。

トレイト境界という日本語はわかりにくい。

bound という英語には、境界線によって区切られた一定の領域の内側、という、「面」的な意味があるようで、 おそらくその意味で bound を使っているんだろうけど、 境界という日本語から最初に想起されるイメージは、領域を区切る一本の線だと思う。なので、意味の脱落が生じている気がする。 (境界をキョウガイと読む場合は、領域に近い意味もあるようだけど、境界はキョウカイと読むほうが多いだろうし)

"デストラクト" → 分割代入 とかのほうがよさそう。

destructuring の日本語訳。

”デストラクト" より、分割代入とかの方がいいな。 そもそも、destructuring と destruct とは、英語でも意味が異なるようなので、destructuring を "デストラクト" と訳すのは単純に間違っていそうな気がする。

destructuring は

fn main() {
    let arr = [100, 200];
    let [first, second] = arr;
    println!("Hello, world! {first}, {second}");
}

みたいなコードのこと。(上のコードでは、配列の内容を first と second という変数に代入している)

JavaScript では分割代入(↗)
またはそのままデストラクチャリングと訳されている様子。

C++ では構造化束縛(structured bindings)(↗)という。

デストラクトというと、C++プログラマからみると Destructor (クラスの破棄関数)と思っちゃうので困る。
ちなみに、Rustで C++ の Destructor に対応するのは Drop トレイトの drop() メソッド。

*1:不適切な命名:関数などに名前をつけるときは、名は体を表すように、わかりやすい名前をつけるべき、という考え方に反しているという意味です。

Rust Memo

Rust 本家

Rust本家

Rustプログラミング言語

ドキュメント

Rustを学ぶ - Rustプログラミング言語

標準ライブラリのリファレンス

std - Rust

Playground

Rust Playground

Rust By Example (RBE)

Introduction - Rust By Example

.

この記事は主に RBE をベースにしている。

Rustインストール後は rustup doc でローカルのドキュメントをブラウザで表示できる。

Hello World など

Hello World

fn main() {
    pirntln!("Hello World!");
}

FizzBuzz

fn main() {
    fizz_buzz();
}

fn fizz_buzz() {
    for i in 1..=100 {
        let mut s = "";
        if i % 15 == 0 {
            s = "FizzBuzz";
        }
        else if i % 3 == 0 {
            s = "Fizz"
        }
        else if i % 5 == 0 {
            s = "Buzz"
        }
        if (s.len() != 0) {
            println!("{}: {}", i, s);
        }
    }
}

Rustc

rustc は、Rust のコンパイラ
通常は直接使わず、 cargo 経由で実行される。

Cargo

cargo (カーゴ)は Rust のプログラムをビルドするツール。
Rust の外部パッケージ(crate (クレート)と呼ばれる)のパッケージマネージャでもある。
crate は https://crates.io で公開されている。
Cargo.toml に、追加で必要な crate を記述しておくと cargo はビルドに先立ち crate.io からその crate をダウンロードする。
(※ 自分で書くプログラムも crate と言う)

$ cargo new my_app             # 作業環境の作成
$ cargo new --lib my_lib       # ライブラリ作成用作業環境の作成
$ cd my_app
$ cargo build                  # デバッグビルド ==> target/debug/my_app ができる。
$ cargo build --release        # リリースビルド ==> target/release/my_app ができる
$ cargo check                  # ビルドせず、コードのチェックだけしてビルドはしない。
$ cargo run                    # 実行 (./target/debug/my_app でも実行できる)
$ cargo doc                    # target/doc にドキュメントを生成
$ cargo -h                     # cargo の使い方
$ cargo build -h               # cargo build の使い方

コメント

コメント

// ラインコメント
/* ブロックコメント */
/*
 * ブロックコメント
 */

Doc Comment

/// で Doc コメントを書くことができる。 関数や、構造体に Doc コメントを書いておくと、cargo doc で生成されるドキュメントにその記述が反映される。

生成される場所: target/doc/my_app/index.html

Doc コメント内では Markdown がサポートされる。

Documentation - Rust By Example

/// 四角形を表す構造体
struct Rectangle {
    left:   u32,
    top:    u32,
    width:  u32,
    height: u32,
}

文字列のフォーマットとプリント

Rust では、コンソールへの出力や、文字列のフォーマットは println! や format! といったマクロを使う。(! がつくものはマクロ)

Rust の関数は現状可変個引数に対応してないのでマクロでやるしかない。

format!

String 型の文字列にフォーマット結果を書き込んで返す

print!

format! と同じだけど、標準出力にプリントする

println!

print! と同じだけど、改行も出力する

eprint!

標準エラー出力にプリントする

eprintln!

eprint! と同じだけど、改行も出力

fn fmt() {
    let s = format!("{}", "Hello");
    print!("{}\n", s);
    println!("{} World", "Hello");
    println!("{} + {} = {}", 1, 2, 3);
    println!("My name is {0}, {1} {0}.", "Bond", "James");
    println!("My name is {bond}, {james} {bond}.", bond="Bond", james="James");
    println!("dec: {num}, oct: {num:o}, hex: {num:x}, bin: {num:b}", num=15);
    println!("{:?}", ("Hello", "World"));

    #[derive(Debug)]
    struct St(i32);
    println!("{:?}", St(32));
    println!("{:#?}", St(32));
}
Hello
Hello World
1 + 2 = 3
My name is Bond, James Bond.
My name is Bond, James Bond.
dec: 15, oct: 17, hex: f, bin: 1111
("Hello", "World")
St(32)
St(
    32,
)

Format 記法いろいろ

"{:?}"デバッグ用のフォーマット指定。

"{:#?}" も同じだが、構造体のメンバなど、見やすく表示してくれる(Pretty Print)

デバッグ用のフォーマットは、デフォルトでは、標準ライブラリ std が提供する型にしか対応していない。

struct や enum で作るユーザー定義型は fmt::Debug トレイトを実装しないといけないのだが #[derive(Debug)] とすることで自動的に実装を提供できる。

(必要な時に自動的にやってくれるようになるとよいのだけど)

"{}" に対する有効なテキストは fmt::Display トレイトを自分で実装する必要がある。

Debug

Display

プリミティブ

スカラー

符号付き整数
i8
i16
i32
i64
i128
isize           /// ポインタのバイト数ぶんの幅をもつ

小数点以下がない数値はデフォルトで i32 になる。

符号無し整数
u8
u16
u32
u64
u128
usize           /// ポインタのバイト数ぶんの幅をもつ
浮動小数点数
f32
f64

小数点以下を含む数値はデフォルトで f64 になる

文字型(Unicode
char
bool型
bool
ユニット型
()

空のタプルだけがユニット型になれる。何もないことを表すのに使われる。

複合型

配列
[1, 2, 3]

配列は同じ型のデータを複数保持する

タプル
(1, true)

タプルは異なる型のデータを複数保持する

変数に使う型

変数を宣言するとき、: <型名> という形で、変数名の後ろに型を指定してもよい。指定しなくても多くの場合は型推論(inference)が働いて自動的に型が決定される。

fn variable() {
    // 型を指定してもよい
    let result: bool = true;

    // なくてもよい(true を代入しているので bool 型とわかる)
    let result = true;

    // 数値はデフォルトで i32
    let num = 15;

    // 小数点付きの数値はデフォルトで f64
    let float = 100.0;

    // 型サフィックス付き数値をいれるとその型になる。
    let unsigned = 15u32;

    // 型推論システムが見ているのは初期値だけではない。
    let mut var = 100;               // i32 型のように思えるが、この段階ではまだ型は未確定で…
    var = 1_000_000_000u64;          // ここで u64型が確定する。

    // 下記のコードはエラー。型は途中で変更できない。
    // var = true;

    // でも、同じ名前の変数を新たに導入して以降のコードで古い var を見えなくする
    // こと(シャドーイング)で型を変更したように見せかけることは可能。
    let var = true;
}

リテラル (Literals)

Literals and operators - Rust By Example

タプル (Tuples)

Tuples - Rust By Example

配列とスライス (Arrays and Slices)

Arrays and Slices - Rust By Example

カスタムタイプ (Custom Types)

Custom Types - Rust By Example

カスタムタイプは 2つ。struct と enum

構造体 (Struct)

Structures - Rust By Example

列挙型 (Enum)

Enums - Rust By Example

use宣言

use - Rust By Example

Cのような Enum も可能

C-like - Rust By Example

Enum を使ったリンクトリスト

Testcase: linked-list - Rust By Example

定数 (constants)

constants - Rust By Example

変数の束縛 (Variable Bindings)

Variable Bindings - Rust By Example

let

変数はデフォルトでは書き換え不可 (Mutability)

Mutability - Rust By Example

スコープとシャドーイング (Scope and Shadowing)

Scope and Shadowing - Rust By Example

初期化の遅延 (Declare first)

Declare first - Rust By Example

滅多に使わない

初期化忘れのコンパイルエラーになりやすい

変数を一時的に不変にする (Freezing)

型 (Types)

Types - Rust By Example

  • 組み込み型を別の組み込み型に変換する (Casting)
  • リテラルで型を指定
  • 型推論 (Type Inference)
  • 型に別名をつける (Aliasing)

組み込み型を別の組み込み型に変換する (Casting)

Casting - Rust By Example

リテラルで型を指定 (Literals)

Literals - Rust By Example

数値リテラルは、接尾辞として型を指定することで、型を明示することができる。 これをアノテーション(annotation:注釈) と呼んでいる様子。

let a = 255u8;
let b = 0x100u16;

アノテーションがない場合、その変数の使い方をもとに型推論によって型が決まる。
なんら制約がない場合は、整数は i32 、浮動小数点数は f64 になる。

型推論 (Inference)

Inference - Rust By Example

Rust の型推論エンジンはかなり頭がよいので、初期化コードだけでなく、初期化後の変数の使い方も見て型を決定する。

// 型アノテーションによって elem が u8 であることはこの場で確定。
let elem = 5u8;

// 空のベクタ(可変長の配列)
let mut vec = Vec::new();
// この時点では、まだベクタに格納される型は未確定なので、vec の型も未確定。
// コンパイラは、なんからのベクタであることだけを知っている。(Vec<_>)

// ベクタに最初の要素をセット
vec.push(elem);
// ここで初めてベクタの型が確定する。(Vec<u8>)

型に別名をつける (Aliasing)

Aliasing - Rust By Example

  • type で型に別名をつけることができる。
  • 型は UpperCamelCase でないとウォーニングが出る。
  • #[allow(non_camel_case_types)] というアトリビュートを指定すると、キャメルケース以外でもOK
type UInt = u32;

#[allow(non_camel_case_types)]
type uint32_t = u32;

type の目的は、ボイラープレート (boilerplate) なコードを減らすため。
ボイラープレートとは、言語仕様からくる要請によって、プログラマが繰り返し書かされる同じコードのこと。
ジェネリックを使うと、型がどんどん複雑になるので短い名前をつける機能は必須になってくる。

アトリビュート (Attribute: 属性)

Attributes - Rust By Example

アトリビュートは、クレート、モジュール、アイテム(関数や構造体など)につけるメタデータ(追加情報)。

下記の用途で使う。

  • 条件付きコンパイル
  • クレートの名前、バージョン、タイプ(binary or library)を指定する
  • ウォーニング(lints)の抑止
  • コンパイラの機能(マクロ、globインポートなど)を有効にする
  • 外部ライブラリとのリンク
  • 関数を Unit テスト用とマークする
  • 関数に、ベンチマークの一部になるもの、とマークする

クレート全体に適用する場合はファイルの先頭などで #! をつけて指定する。(#! は Inner attributes)

#![cfg(...)]

モジュールやアイテム(関数や構造体など)につける場合は ! をつけずアイテムの前に置く。(# は Outer attributes)

#[cfg(...)]
fn func() {}

アトリビュートには、いくつかの書き方で設定値を指定できる

#[attribute = "value"]
#[attribute(key = "value")]
#[attribute(value)]

アトリビュートは複数の値を持つこともできる。

#[attribute(For, Bar, Buz)]

複数行に渡って記述することも可能。

#[attribute(value0, value1, value2, value3,
            value4, value5, value6, value7)]

アトリビュートの詳細はこちら
Attributes - The Rust Reference

使わないコードへの warning を抑止するアトリビュート(dead_code)

dead_code - Rust By Example

コンパイラは使われていない関数には unused function とウォーニングを出す。 下記のアトリビュートを指定することで抑止できる。(ただ、現実の開発では使われていない関数は削除するほうがよい)

#[alllow(dead_code)]
fn unused_func() {
}

クレート全体に適用するには ! 付きで書いておく

#![allow(unused)]

TBD: あれ dead_code と unused の違いは?

クレートにつけるアトリビュート

Crates - Rust By Example

crate_type アトリビュートでクレートがバイナリクレートか、ライブラリクレートを指定する。 ライブラリのタイプも指定できる。(TBD: それなに?)

#![crate_type = "lib"]

crate_name アトリビュートでクレートの名前を指定する。

#![crate_name = "RingBuffer"]

ただし Cargo を使う Rust プロジェクトでは crate_type, crate_name は意味を持たない。(Cargo.toml で管理するためかな?)

たいていの Rust プロジェクトとは Cargo を使って管理するので、これらのアトリビュートはほとんど使われない様子。

crate_type アトリビュートを使うときは rustc--crate-type オプションは必要ない。

$ rustc mylib.rs
$ ls lib*
libmylib.rlib

crate_type アトリビュートがない場合 rustc がエラーになる

$ rustc mylib.rs
error[E0601]: `main` function not found in crate `mylib`
 --> mylib.rs:3:1
  |
3 | / pub fn hello_mylib() {
4 | |     println!("mylib");
5 | | }
  | |_^ consider adding a `main` function to `mylib.rs`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0601`.

cfg アトリビュート

cfg - Rust By Example

cfg アトリビュートで、条件コンパイルができる。

条件付きコンパイルを制御する方法は cfg アトリビュートのほか、cfg_attr アトリビュートと cfg! マクロがある。

cfg アトリビュートによる条件コンパイル

通常は、条件コンパイルはコードの可読性を下げるのでアプリケーションを書くときは使わないように書くのが正しい。

が、HWリソースを節約したいときなどには、動かさないコードは実行バイナリに含めたくないので必ず必要になる機能。

#[cfg(target_os = "linux")]
fn who_am_i() {
    println!("Linux");
}

#[cfg(target_os = "windows")]
fn who_am_i() {
    println!("Windows");
}

/// any: foo または bar が定義されている場合コンパイルされる
#[cfg(any(foo, bar))]
fn needs_foo_or_bar() {
}

/// all: unix ファミリー、かつ、32bitアーキテクチャの場合のみ
#[cfg(all(unix, target_pointer_width = "32"))]
fn memcpy4byte() {
}

/// not: foo が定義されていないこと。
#[cfg(not(foo))]
fn need_not_foo() {
}

cfg_attr アトリビュート

cfg_attr アトリビュートは、第一引数が真なら、第二引数以降をそれぞれ '#[' と']' で囲んでアトリビュートを作り、自らをそのアトリビュートで置き換える。

うーん。うまい使い方が思いつかない。

cfg!() マクロによる条件コンパイル

fn main() {
    cond_print();
}

fn cond_print() {
    let endian = if cfg!(target_endian = "big") {
        "Big Endian"
    }
    else if cfg!(target_endian = "little") {
        "Little Endian"
    }
    else {
        "??"
    };
    println!("{}", endian);   
}

cfg アトリビュートで任意のオプション(Configration Option)を使う方法

rustc--cfg オプションで任意の値を渡すことが可能。

$ rustc --cfg 'verbose' --cfg 'my_option="foo"'

この例では、コード中のアトリビュート #[cfg(verbose)]#[cfg(my_option="foo") が有効になる。

feature オプションは Cargo で予約

feature オプションは Cargo によって設定される Configuration Option なので、使わない方がよさそう。

Cargo.toml で [features] を設定する方法はこちら
Features - The Cargo Book

組み込みの cfg オプション

  • target_arch
  • target_feature
  • target_os
  • target_family
  • unix
  • windows
  • target_env
  • target_endian
  • target_pointer_width
  • target_vendor
  • test
  • debug_assertions
  • proc_macro

詳細はこちら
Conditional compilation - The Rust Reference

モジュール














WSL に最新の ubuntu をいれる(emacsも apt install した)

Ubuntu 18.04 LTS というのを使っていたが、Windowsストアにバージョン無しの Ubuntu があるのでこっちも入れた。中身は Ubuntu 20.04 LTS らしい。 これまでバージョンがあがると別アプリになっていたけど、一本化したのかな。


1. Windows の機能の有効化または無効化で WSL にチェックをつける。

もうチェック済みだけど、はじめて入れる場合はやっておく必要がある。再起動必要らしい。


コントロールパネルのプログラムから実行できる。

f:id:nodamotoki:20200627135558p:plain
コントロールパネル>プログラム


Windows Subsystem for Linux にチェックを付ける。

f:id:nodamotoki:20200627142018p:plain
Windows の機能の有効化または無効化 ダイアログボックス


2. Microsoft ストアから Ubuntu をインストール

スタートメニューをクリックして 「store」と入力すると 「Microsoft Store」が出てくるので実行。

f:id:nodamotoki:20200627140523p:plain
スタートメニュー


右上の虫眼鏡アイコンをおして「ubuntu」で検索

f:id:nodamotoki:20200627140812p:plain
Microsoft Store で ubuntu を検索


検索すると、いくつかヒットする

f:id:nodamotoki:20200627142430p:plain
ubuntu で検索したところ


一番左の「Ubuntu」をクリックすると下記画面になるのでインストールする。(もうインストール済みなので下図では「インストール」ボタンがないが、まだの場合は「インストール」ボタンがある)

f:id:nodamotoki:20200627141334p:plain
Ubuntu アプリのページ


インストール後、「起動」ボタンの右の「…」をクリックすると、「スタートにピン留めする」「タスクバーにピン留めする」があるので、とりあえずピン留めしておいた。(下図では文字色が灰色になってい見にくいが)

f:id:nodamotoki:20200627141621p:plain
スタートとタスクバーにピン留め


3. ubuntu の実行

タスクバーにあるアイコン f:id:nodamotoki:20200627142716p:plain をクリックとターミナルが起動する。

最初はユーザー名とパスワードの入力を求められる。 このユーザー名とパスワードは、あとで sudo とか root 権限で実行する必要があるときに使う。 入ってないソフトを apt でインストールするのにも必要。

Windows のユーザー、パスワードとは同じでなくてもよいらしい。


4. emacs のインストール

vim はシステム管理ツールという側面もあるで初めから入っているけど emacs は入っていないのでインストール。

まずは現状での最新版の取り込み。sudo するとパスワードを聞かれるので、ubuntu を最初に起動したときに入れたパスワードをいれる。

$ sudo apt upgrade    # list を最新に更新するらしい
$ sudo apt update     # いまあるソフトを最新に更新

どんなソフトがあるか確認するのは

$ apt list            # 一覧表示 (多いので apt list | less としたほうがいいかな)
$ apt list emacs      # emacs だけ表示
$ apt list *emacs*    # ワイルドカードも使えるらしい
$ apt search emacs    # 説明欄に emacs という文字列をふくむパッケージを検索
$ apt show emacs      # emacs パッケージの説明を表示

emacs のインストール

$ sudo apt install emacs

新しい consel-gtags.el を試してみたらパッチが必要だった

こまった

counsel-gtags--select-file-ivy-parameters でコールしている string-empty-p という関数がないようで動かない。

Symbol’s function definition is void: string-empty-p

とエラー終了。

対応

『やさしい Emacs-Lisp講座』を参考に scratch バッファで

  1. (setq debug-on-error t) <C-j> として、その後
  2. counsel-gtags-dwim を実行(タグジャンプしてみただけ)
  3. バックトレースが表示される。みると counsel-gtags--select-file-ivy-parameters でエラー発生。
  4. クリックしてエラー発生関数に飛び M-x edebug-defun する
  5. もう一度実行させて関数内をステップ実行。

として確認した。

(if (string-empty-p tagname) ...)

という記述を変更する。↓

(if (= (length tagname) 0) ...)

長さが 0 の判定にした。

修正後

emacs-lisp-byte-compile-and-load

でバイトコンパイルして再ロード。

最後に edebug-defun の効果を消すために対象関数の中で

C-M-x (eval-defun)

を実行。これでデバッグ機能なしのふつうの関数が再定義されるらしい。

参考資料

www.amazon.co.jp

おわりに

以前も file-truename を呼ぶためにシンボリックリンクのファイルが開けない・・・という動きになってしまい file-truename を使わないようにしたことがあった。今回はそれは大丈夫なのかな??

2次方程式の新しい解法のおためし実装

2次方程式の新しい解法

↓ こんな記事を見かけた

天才数学者が二次方程式の簡単な解き方を考案!「推測も暗記も必要ない」 | ナゾロジー

計算だけで二次方程式が解けるそうな。

プログラムにしてみる

計算だけでできるならコンピュータ向き。 ためしにそのアルゴリズムpython で書いてみた。

niji.py

#!/usr/bin/python3

import os
import sys
import math

# 二次方程式の計算
# @param a:     x^2 の項の係数
# @param b:     x   の項の係数
# @param c:     定数項
#
# @ref https://nazology.net/archives/49629
#      https://arxiv.org/abs/1910.06709
#
# @note 計算の概要
#       x^2 - bx + c = 0 の二次方程式を (x-p)(x-q) の形にする。
#       このとき p = b/2 + u, q = b/2 - u となる未知数 u を考える。
#       すると
#       (x-(b/2+u))(x-(b/2-u)) = 0
#       となる。このとき解は
#       x = b/2 + u, b/2 - u の2つ。
#       これを ax^2 + bx + c = 0 で考えると解は
#       x = -b/2a + u, -b/2a - u       ...(1)
#       である。 c は 2 つの解の積なので
#       c = (-b/2a + u)(-b/2a - u)
#       となり、これを計算すると未知数 u が得られる。
#       c = (-b/2a)^2 - u^2
#       u = sqrt((-b/2a)^2 - c)
#       あとは (1) 式に u を代入することで解を得る。
#       x = -b/2a + sqrt((-b/2a)^2 - c),
#           -b/2a - sqrt((-b/2a)^2 - c)
def quadratic_equation(a, b, c):
    center = (-1 * b) / (2 * a);
    gap    = math.sqrt(center ** 2 - c)
    return center + gap, center - gap

def print_qe(a, b, c):
    ans1, ans2 = quadratic_equation(a, b, c)
    print("ans1: ", ans1)
    print("ans2: ", ans2)

def test():
    print_qe(1,  2, 1)
    print_qe(1, -2, 1)
    print_qe(1,  4, 4)
    print_qe(1, -4, 4)
    print_qe(1,  6, 9)
    print_qe(1, -6, 9)

    print_qe(1, 10, 18)
    print("5+sqrt(7): ", 5+math.sqrt(7))
    print("5-sqrt(7): ", 5-math.sqrt(7))

if __name__ == "__main__":
    test()
    

ふむふむ。 とりあえず動いてそう。

解の公式との違い

式を変形すると二次方程式の解の公式と同じになるらしい。

解の公式をプログラム化してみると・・・

# 解の公式
# x = (-b + sqrt(b^2 - 4ac)) / 2a,
#     (-b - sqrt(b^2 - 4ac)) / 2a
def quadratic_equation_official(a, b, c):
    center  = -1 * b
    gap     = math.sqrt(b**2 - 4 * a * c)
    divider = 2 * a
    return (center + gap) / divider, (center - gap) / divider

プログラム化するときは割り算が一回で済む分、解の公式をそのままプログラム化するより、新しい方式のほうが有利かな。新しい方式のほうがかけ算の回数も少ないかな。

Windows10 で pip install がエラーになる例の件

本を買った

東京大学のデータサイエンティスト育成講座 Pythonで手を動かして学ぶデ―タ分析

説明にあるとおりに Anaconda をインストールして、 condas-datareader と Plotly を pip install でインストールしようとしたら pip install がエラーを吐く。

その回避方法

環境変数 Path に Anaconda3\Library\bin を追加する。 (但し Anacondaのインストール先はインストールしたときの設定によって変わるようですよ)

f:id:nodamotoki:20191022010747p:plain
環境変数 Path に Anaconda\Library\bin を追加

うちはこれで

pip install condas-datareader
pip install Plotly

がうまくいきました。(pip が Successfully installed と言ってる)

Special Thanks

下記のサイトを参考にさせていただきました。

https://github.com/conda/conda/issues/8046#issuecomment-456565256

https://teratail.com/questions/176363

https://qiita.com/hasht/items/29a0a1bdb665e356ce66

Cygwin で java を動かす

Cygwin には java は付属してないみたい

setupで検索しても出てこない。

f:id:nodamotoki:20190120042957p:plain
cygwin の setup で java を検索している画

でもWindows でインストールしていれば使える

$ type java
java は /cygdrive/c/ProgramData/Oracle/Java/javapath/java です

でも実行すると動かない

java -Dfile.encoding=UTF-8 -jar /usr/share/emacs/26.1/lisp/contrib/scripts/ditaa.jar  /tmp/babel-cacHaR/ditaa-4QhER5 /home/someone/org/images/hello-world.png
Error: Unable to access jarfile /usr/share/emacs/26.1/lisp/contrib/scripts/ditaa.jar

なぜなら、PATH が Windows 形式じゃないから

java に限らないが、ふつうのWindowのプログラムは、引数で受け取る PATH が Windows の PATH 形式じゃないと、うまく動けない。

Cygwin から実行されることは想定していないので、コマンドラインに指定した /usr/... とか、いったいどこだ? と混乱する。

なので変換スクリプトを用意

#!/usr/bin/python3
# -*- python -*-
#
# Windows 向けの Java を実行する前に引数のファイル名を
# Windows 向けの java が理解できるPATHに直す。
#

import os
import sys
import re
import subprocess

JAVA_FOR_WINDOWS = "/cygdrive/c/ProgramData/Oracle/Java/javapath/java"

def main():
    argv = sys.argv[1:]
    #print(argv)
    option = []
    args = []
    for arg in argv:
        if arg.startswith("-"):
            option.append(arg)
        else:
            args.append(arg)
    #print(option)
    #print(args)
    args_win = []
    for arg in args:
        cmdline = "cygpath -m " + arg
        #print(cmdline)
        ret = subprocess.check_output(cmdline, shell=True)
        ret = ret.decode()
        ret = ret.strip();
        args_win.append(ret)
        #print("args_win=", args_win)
    argv = option + args_win

    #print("argv=", argv)
    cmdline = JAVA_FOR_WINDOWS + " " + " ".join(option) + " " + " ".join(argv)
    #print("cmdline=", cmdline)
    subprocess.check_call(cmdline, shell=True)

if __name__ == "__main__":
    main()

これを ~/bin/java として保存。実行権つけて本物の java より PATH 環境変数の前に置いといて先に実行されるようにしとく。

$ chmod 755 ~/bin/java
$ export PATH=~/bin:$PATH

これで、java を実行するとこのスクリプトが実行され、スクリプトの中でほんものの java を実行する前に PATH をうまいこと変換する。

以上。