Roiban1344's Logbook

第12日目 - The book 第10.2節をよむ - ジェネリクス

sin1

264sin12^{64} \sin 1 の整数部分を知るためにはテイラー展開の何次の項まで足せばよいか.

20!=2432902008176640000<264=18446744073709551616<21!=51090942171709440000\begin{align} 20!=2432902008176640000&\\ <2^{64}=18446744073709551616&\\ <21!=51090942171709440000& \end{align}

だから,真値に対する誤差を1より小さくするには2020次までは必要.sin\sin のテイラー展開は偶数項しかないから実際には 1919 次まで足して,

11!13!+15!119!=102360822438075317121645100408832000=a20\frac{1}{1!}-\frac{1}{3!}+\frac{1}{5!}-\cdots -\frac{1}{19!} =\frac{102360822438075317}{121645100408832000} =a_{20}

sin1\sin 1 との誤差は 1/21!1/21! 以下になる.

b20=a20+121!=4299154542399163314151090942171709440000b_{20} = a_{20} + \frac{1}{21!} = \frac{42991545423991633141}{51090942171709440000}

とすると,sin1[a20,b20]\sin 1\in [a_{20}, b_{20}]. 実際に計算することで

264a20=264b20=15522399902203605024\lfloor 2^{64}a_{20}\rfloor = \lfloor 2^{64}b_{20}\rfloor = 15522399902203605024

が分かって,

264sin1=15522399902203605024\lfloor 2^{64}\sin 1\rfloor = 15522399902203605024

と結論付けられる.この数をAAとする.多少注目されてもよさそうな値だが,これでグーグル検索すると荷物検索を提案されただけだった.

同様に cos\cosは,

264cos1=9966818358784711825\lfloor 2^{64}\cos 1\rfloor = 9966818358784711825

でこちらはBBとする.

sin1[A264,A+1264],cos1[B264,B+1264],\begin{align} \sin 1 &\in \left[ \frac{A}{2^{64}}, \frac{A+1}{2^{64}} \right],\\ \cos 1 &\in \left[ \frac{B}{2^{64}}, \frac{B+1}{2^{64}} \right],\\ \end{align}

を使って区間演算をする.

倍角:

sin2=2sin1cos1[16773576919535988475264,16773576919535988479264],cos2=cos21sin21[7676554190868976274264,7676554190868976271264],\begin{align} \sin 2 = 2\sin 1 \cos 1 &\in \left[ \frac{16773576919535988475}{2^{64}}, \frac{16773576919535988479}{2^{64}} \right],\\ \cos 2 = \cos^21-\sin^21&\in \left[ \frac{-7676554190868976274}{2^{64}}, \frac{-7676554190868976271}{2^{64}} \right],\\ \end{align}

……みたいなことをしていく.2642^{64}同士の積はi128でオーバーフローするからダメだな.2632^{63}を分母とする分数の構造体を定義して,さらにそれらを両端に持つ「区間」を構造体として定義する.すると自前の構造体+numクレートの関数くらいで 232sini\mid 2^{32} \sin i\mid1i641\leq i \leq 64 の範囲では求まるはず.加法定理に使う乗算は高々6回で済むので,掛け算1回で区間の幅が4倍に広がるとして足りる.

というのはメモ書きに留めて一旦Rust本読む.構造体のコピーを理解していないときっと躓く.

Traits: Defining Shared Behavior - The Rust Programming Language

Traitは型が持つ機能を規定する.他の言語のinterfaceと似ているが,少し違う.

トレイトの定義中にはメソッドのシグネチャのみを書く.

構造体 implements トレイトではなくてimpl トレイト for 構造体か.

pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self, location)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

これをlib.rsに定義して,summarizeメソッドをmain.rsで使おうとするとuse traits::Summaryが必要になった.どういうことだろう.summarizepubでないのがいけないのかと思って足すとこれも怒られる.

ある型にトレイトをimplementさせるには,その型かトレイトの少なくともどちらか一方がローカルに定義されている必要がある.どちらも外部の場合は許されない.これをcoherenceorphan ruleという.どこかで勝手にimplementが行うことは破壊的であるため.

トレイトの関数にはシグネチャのみを記述する代わりに,デフォルト実装を書くこともできる.このデフォルト実装中には,トレイトが持つ他の関数を利用できる.

あるトレイトをimplementしている型の値を引数に取りたいとき,impl Traitが型として扱える.

pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

これだけなら型パラメータTがいらない.実際にはTのある方法,trait boundを使った記法のシンタックスシュガーになっている.

pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

同じ型の引数が複数ある時はこっちのほうが簡潔に書ける.使いよう.

Specifying Multiple Trait Bounds with the + Syntax

複数のtrait boundを課す場合,+でトレイトをつなげる.記号は+だがだいたい積集合の意味になるはず.

<T: Trait>はシグネチャの直後にwhereを付けて書いてもよい.

関数の戻り値をimpl Traitにすることもできる.これはクロージャやイテレータを使うときに役立つ. ただし共通のトレイトをimplementしているからといって複数の型を返すことはできない.

Fixing the largest Function with Trait Bounds

戻ってきた.>std::cmp::PartialOrdトレイトに定義されているが,これはpreludeに含まれるため明示的にスコープに入れる必要がなかった. Copyトレイトをtrait boundに設定したが,Cloneトレイトとcloneメソッドで代用もできる.または戻り値を参照に変えてもよい.

Try implementing these alternate solutions on your own!

Implemented.

fn largest2<T: PartialOrd + Clone>(list: &[T]) -> Option<T> {
    if list.len() == 0 {
        return None;
    }
    let mut largest = list[0].clone();
    for item in list {
        if *item > largest {
            largest = item.clone();
        }
    }
    Some(largest)
}

fn largest3<T: PartialOrd>(list: &[T]) -> Option<&T> {
    if list.len() == 0 {
        return None;
    }
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    Some(&largest)
}

3が一番自然だな.スライスで受け取っているのだからコピーして返す必要がない.

Using Trait Bounds to Conditionally Implement Methods

構造体がジェネリック型で定義されているとき,trait boundを設けたメソッドを定義できる.

さらに,あるtrait boundを持つ任意の型に対してトレイトのimplementationを行うことをblanket implementationという.

impl<T: Display> ToString for T {

}

初めて見ると一瞬戸惑う.Tという型にToStringトレイトに規定されたメソッドが実装されていて,このTにはDisplayがtrait boundとして設けられている.具体的な型はしていされていないが,ToStringのimplementationを行うためにはDisplayのメソッドを利用するということになる. Displayをtrait boundにもつ全ての型がToStringをimplementしていることにもなる.つまりdisplayさえできればto_stringもできる.

Blanket implementations appear in the documentation for the trait in the “Implementors” section.

これか.

ToString in std::string - Rust

今日はここまで.次のライフタイムが難しそー…….