21/11/21 18:59:28.56 a8amZ/lG.net
更にtimes()自体をイテレータにしてしまえば汎用的になるだけでなく
それらトレイト境界などのコードの記述も魔法のように消えて短くなる
fn main() {
let n = 5; // 任意の整数型
n.times().for_each(|n| println!("OK {}", n));
}
trait Times<T: Sized> {
fn times(self) -> std::ops::Range<T>;
}
impl<T: num::Zero> Times<T> for T {
fn times(self: T) -> std::ops::Range<T> {
num::Zero::zero()..self
}
}
これだけで動作する
89:デフォルトの名無しさん
21/11/21 20:22:13.92 ekMm5ue5.net
timesなら精々u64::MAX回も繰り返すことなさそうだしTからu64に変換するのではだめなの?
90:デフォルトの名無しさん
21/11/21 20:32:22.18 VYuGYhJz.net
トレイト境界が短くなって素晴らしい原因ですがもしかして
>>83では要のops::Range<T>型がコードに明示されてないので条件のiter::Stepを要求されてしまい
>>85ではそのops::Range<T>型をトレイト境界に登場させたためiter::Stepが不要となり
>>87ではそのops::Range<T>型を返り値として明記したためトレイト境界にも不要となった??
91:デフォルトの名無しさん
21/11/22 10:01:45.21 YMaXH3oe.net
指定するトレイト境界が減った代わりにAPIが劣化してる
92:デフォルトの名無しさん
21/11/22 11:46:24.84 EEj8G+es.net
>>90
イテレータ版の方がfor_each以外とも組み合わせられるからAPIとして良いと思う
>>87
しかしトレイト境界でnum::Zeroしか求められないのはstd::ops::Range周りの設計がおかしいと思われる
普通に実装すれば初期値(num::Zero)に増分(num::One)を加えて(ops::Add)いって比較(ops::PartialOrd)が必要となる
実際にnum::rangeによるイテレータ版times()の実装は Clone + PartialOrd + num::Zero + num::One となる
fn main() {
let n = 5; // 任意の整数型
n.times().for_each(|n| println!("OK {}", n));
}
trait Times<T: Sized> {
fn times(self) -> num::iter::Range<T>;
}
impl<T: Clone + PartialOrd + num::Zero + num::One> Times<T> for T {
fn times(self: T) -> num::iter::Range<T> {
num::range(T::zero(), self)
}
}
93:デフォルトの名無しさん
21/11/22 12:13:59.22 qBbb57Hy.net
わざわざ外部crateと独自trait使って
n.times().for_each(f)にするくらいなら
(0..n).for_each(f)で十分
94:デフォルトの名無しさん
21/11/22 12:33:40.60 EEj8G+es.net
>>92
それでは最初の条件のジェネリックを満たせていない
>>88
状況によってはそのように強引にu64へ変換できても対応できなくなるケースもある
例えば単純な例として文字'x'からのみなる文字列による型Xを考えてみよう
#[derive(Debug,Clone,PartialEq,PartialOrd)]
struct X(String);
impl X {
fn new(s: &str) -> Self {
if !s.chars().all(|c| c == 'x') {
panic!("not x");
}
X(s.to_string())
}
}
これで文字'x'以外は使えない文字列の型が出来上がり
あとは>>91で必要なZeroとOneとAddを定義すれば動くはず
impl num::Zero for X {
fn zero() -> X { X::new("") }
fn is_zero(&self) -> bool { self.0 == "" }
}
impl num::One for X {
fn one() -> X { X::new("x") }
}
impl std::ops::Add for X {
type Output = X;
fn add(self, rhs: X) -> X { X(self.0.clone() + &(rhs.0)) }
}
95:デフォルトの名無しさん
21/11/22 12:45:37.77 EEj8G+es.net
>>93の続き
ところがnumクレートのOneは不必要に掛け算のMulも要求してきた
仕方ないので呼び出したらパニックするimplを加える
impl std::ops::Mul for X {
type Output = X;
fn mul(self, _rhs: X) -> X {
panic!("mul() for X")
}
}
さらになぜかnum::ToPrimitiveも要求してきたのでこれもパニック実装する
impl num::ToPrimitive for X {
fn to_i64(&self) -> Option<i64> {
panic!("to_i64() for X")
}
fn to_u64(&self) -> Option<u64> {
panic!("to_u64() for X")
}
}
これで>>91のnum::range利用イテレータ版times()が動くはず
そういえばDisplay実装を忘れたのでDebug表示
fn main() {
let n = X::new("xxxxx");
n.times().for_each(|n| println!("OK {:?}", n));
}
実行結果:
OK X("")
OK X("x")
OK X("xx")
OK X("xxx")
OK X("xxxx")
ちゃんと数値型以外でも動きました
96:デフォルトの名無しさん
21/11/22 15:04:22.59 UzgCqcLK.net
>>93
>それでは最初の条件のジェネリックを満たせていない
ジェネリックにしたければ(T::zero()..n).for_each(f)と書けばいいだけでしょ
単にRangeを返すだけのメソッドを手間かけて微妙に抽象化しても周りが迷惑するだけだぞ
97:デフォルトの名無しさん
21/11/22 15:23:28.68 EEj8G+es.net
>>95
ところがT::zero()..nだと動かない
さきほどの>>93の型Xはnum::range利用だと動いたが
let n = X::new("xxxxx");
(X::zero()..n).for_each(|n| println!("OK {:?}", n));
としようとすると以下のコンパイルエラー
the following trait bounds were not satisfied:
`X: Step`
つまりnightlyでないと使えないstd::iter::Stepを満たしていないと言われる
98:デフォルトの名無しさん
21/11/22 19:21:50.63 fRCpO7Rh.net
どうしてもstableでやりたいという話ならRangeとStepを独自に用意するしかなさそう
n..mという表記は使えないが、n.times()なら支障なく実装できるかと
自分なら以下みたいに書くけどね
(T: From<i32> + PartialOrd が前提)
(0..).map(T::from).take_while(move ¦x¦ x<n)
99:デフォルトの名無しさん
21/11/22 19:36:28.33 3Rtka3dv.net
なるほどね
100:デフォルトの名無しさん
21/11/22 19:55:09.41 5egSOJea.net
>>97
それだとT: From<i32>という無関係で不要な強い条件を満たさないといけないため
例えば>>93の型Xでは動かないね
シンプルにZero、One、Add、PartialOrdだけで実装したほうが良さそう
101:デフォルトの名無しさん
21/11/22 20:04:43.45 VyYbXHRo.net
C++じゃないんだから不毛な型パズルはやめろ
102:デフォルトの名無しさん
21/11/22 20:25:52.53 gBMgBg1g.net
C++よりマシ
103:デフォルトの名無しさん
21/11/22 20:56:45.47 MJpN6tlo.net
意味のない例で延々とよくやるわ
104:デフォルトの名無しさん
21/11/22 21:02:15.85 FLi/1Joa.net
カスタムな型ならRangeよりイテレータ対応が先
105:デフォルトの名無しさん
21/11/22 21:09:17.05 fRCpO7Rh.net
StepがunstableなのはさておきFromLiteralみたいなトレイトがあるとZeroやOneの出番が減ってうれしいのかね
T: FromLiteralの時に整数リテラルがT型の値として解釈されるようになるようなイメージ
106:デフォルトの名無しさん
21/11/22 21:58:50.69 FLi/1Joa.net
try_into()でできるよ
107:デフォルトの名無しさん
21/11/22 22:09:34.28 fRCpO7Rh.net
言葉足らずでしたね
struct Foo;
が FromLiteral を実装しているときに
let n: T = 123;
というコードを書くとコンパイラが
let n = T::from_literal("123");
といったコードに変換してくれるイメージ
from_literalはconst fnにできてコンパイル時にエラー検出できるとベター
108:デフォルトの名無しさん
21/11/22 22:10:11.00 fRCpO7Rh.net
>>106
TはFooの間違い
109:デフォルトの名無しさん
21/11/23 07:57:08.78 9DtS3af5.net
ねこ
110:デフォルトの名無しさん
21/11/23 10:28:33.41 8Ju98kPx.net
URLリンク(github.com)
Coreチームがクソだからやめたった、ってこと?
111:デフォルトの名無しさん
21/11/23 13:56:03.32 b1gEfTjX.net
const fnが言いたいだけやろ、だれが=演算子でcopyでもなく、言語上ないことになってるコンストラクタでもなく
そんな特異なトレイトを勝手に呼ぶのが嬉しいねん、なにがバター犬や
112:デフォルトの名無しさん
21/11/23 14:58:36.02 s6k3uLQ1.net
>>110
= が特殊な振る舞いをするのではなくて
整数リテラルが組み込み整数型についてgenericな数値になるという振る舞いを
FromLiteralを実装した型に広げようという案のつもりです
113:デフォルトの名無しさん
21/11/23 20:05:14.41 1c3aeddQ.net
m..nをiter::Step使わず素直にPartialOrd + One + Addだけで実装してくれれば汎用的で分かりやすいと思う
struct Range<T> {
start: T,
end: T,
}
fn range<T>(start: T, end: T) -> Range<T> {
Range { start, end }
}
impl<T: Clone + PartialOrd + One + Add<Output=T>> Iterator for Range<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.start < self.end {
let result = self.start.clone();
self.start = self.start.clone() + T::one();
Some(result)
} else {
None
}
}
}
fn main() {
let n :Vec<u8> = range(1, 5).collect();
let x :Vec<X> = range(X::new("x"), X::new("xxxxx")).collect();
println!("{:?}", n); // [1, 2, 3, 4]
println!("{:?}", x); // [X("x"), X("xx"), X("xxx"), X("xxxx")]
}
>>93のX型でも動いたよ
114:デフォルトの名無しさん
21/11/23 20:38:59.43 rocYZd+S.net
特徴もないリテラルを勝手に解釈するとかあり得んわ
現状だって0b0011u32とか0x80u32とか書いてるのに、型定義が横にあるからそれでコンパイル時に
パース処理したいなんてそんな都合の良い言語ちゃうだろ、Raw stringのr"123"とか、Raw bytesとか
とも違うし、確かにfrom_strが"123"を解釈するけどやるにしても、let x: i32 = "123";が通ってから。
でもコンパイル時とはいえ自動型変換に見えるコードは承認しないと思うし、直行性も下がる
こんな場末の酒場みたいな所で言ってもコミッターどころかforと高階関数で揉める駄スレにどうこう出来る内容ちゃう
115:デフォルトの名無しさん
21/11/23 20:58:16.83 Oek7vlRG.net
>>112
数値だからAdd+Oneが成り立つわけで
型Xとか何の意味もないぞ
116:デフォルトの名無しさん
21/11/23 22:14:31.32 qrGqDm2c.net
>>114
でも現実のプログラミングでは数値を生で使うよりも
struct Counter { usize } とか
struct Age { usize } とか
struct XxxLength { usize } とかにして
型が異なることをはっきりさせて安全に使いますよね
そして付加情報があればstructのフィールドが複数になることもあったり
あるいは
117:struct std::time::Durationのようにnanoからsecまで扱えるようにしてもAddをimplして使いますよね つまり生の数値だけを対象にしていては視点が狭いと思うのです
118:デフォルトの名無しさん
21/11/23 22:15:57.50 qrGqDm2c.net
>>115の{ }は( )の誤記ですw
119:デフォルトの名無しさん
21/11/24 03:12:15.58 P1gN11rG.net
Stepがstable入りしたら要らなくなる話になにとんでもない破壊的変更を持ち出しているんだ
120:デフォルトの名無しさん
21/11/24 03:25:07.43 gceGN8+W.net
Age型をn.times().for_each()
Length型をn.times().for_each()
Duration型をn.times().for_each()
ジェネリックw
121:デフォルトの名無しさん
21/11/24 05:02:23.28 e1u6MioL.net
>>118
timesより>>112のm..n rangeイテレータが汎用的でいいんじゃね?
122:デフォルトの名無しさん
21/11/24 15:22:53.32 zkfKZqQ7.net
>>119
times()ですら無駄なのに>>112とかありえないよ
無価値どころか害悪レベルなんだけど
123:デフォルトの名無しさん
21/11/24 17:05:44.23 5wn/1hS5.net
>>117
コンパイラや標準ライブラリの変更で型推論の結果が変わることは破壊的変更扱いされなかったっけ
124:デフォルトの名無しさん
21/11/24 17:26:56.67 Zq3lnaBh.net
>>106
なんとなく分かりましたがいきなりコンパイラが自動変換の前に現状で
例えばまずは整数型を例に絞ってやるとして
trait IntegerCompatible {
type Integer;
fn into_integer(&self) -> Self::Integer;
fn from_integer(n: Self::Integer) -> Self;
}
こんな感じのトレイトにして
まずは利便性のために整数型自体に自分を返すよう実装しておいて
macro_rules! integer_compatible {
($($t:ty)*) => ($(
impl IntegerCompatible for $t {
type Integer = $t;
#[inline]
fn into_integer(&self) -> Self::Integer {
*self
}
#[inline]
fn from_integer(n: Self::Integer) -> Self {
n
}
}
)*)
}
integer_compatible! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
あとは今はコンパイラ支援がないので
使う時に自分でinto_integerして計算などして結果をfrom_integerする感じですかね
125:デフォルトの名無しさん
21/11/24 17:39:27.77 Zq3lnaBh.net
>>122の続き
実際に使ってみる具体例として面倒なので>>112をそのまま使うと
struct Range<T> {
start: T,
end: T,
}
このあたりはそのまま使うとして
fn range<T>(start: T, end: T) -> Range<T> {
Range { start, end }
}
実装部分ではジェネリックT型にOneやAddなどを求められていたのを
CloneとIntegerCompatible要求だけに変えて整数型IにOneやAddなどを移動
impl<T, I> Iterator for Range<T> where
T: Clone + IntegerCompatible<Integer=I>,
I: Copy + PartialOrd + num::One + std::ops::Add<Output=I>, {
type Item = T;
fn next(&mut self) -> Option<T> {
if T::into_integer(&self.start) < T::into_integer(&self.end) {
let result = self.start.clone();
self.start = T::from_integer(T::into_integer(&self.start) + I::one());
Some(result)
} else {
None
}
}
}
今はコンパイラ支援がないので手動変換ですが自動もしくは簡便な記法に出来そう
あと整数型にはいちいちOneやAddやPartialOrdを書かなくても済むように出来そう
126:デフォルトの名無しさん
21/11/24 17:47:29.36 Zq3lnaBh.net
>>123の続き
念のため実際に使う時にどうなるかと使えるかを確認すると
let n :Vec<u128> = range(3, 7).collect();
println!("{:?}", n); // [3, 4, 5, 6]
もちろん整数型自体は使えるのは当たり前なのでLength(usize)で使う場合
#[derive(Debug,Clone)]
struct Length(usize);
impl IntegerCompatible for Length {
type Integer = usize;
fn into_integer(&self) -> Self::Integer {
self.0
}
fn from_integer(n: Self::Integer) -> Self {
Length(n)
}
}
このIntegerCompatible定義はこのような単純形ならマクロ化で出来そうですね
let v :Vec<Length> = range(Length(3), Length(7)).collect();
println!("{:?}", v); // [Length(3), Length(4), Length(5), Length(6)]
そして当然ながら動きました
127:デフォルトの名無しさん
21/11/24 17:56:43.20 Zq3lnaBh.net
>>124の続き
あとは>>93に出てきた変なX型ですね
#[derive(Debug,Clone)]
struct X(String);
impl X {
fn new(s: &str) -> Self {
if !s.chars().all(|c| c == 'x') {
panic!("not x");
}
X(s.to_string())
}
}
と定義はそのまま使っておきます
あとはOneやAddの実装はをせずにIntegerCompatibleだけ実装
impl IntegerCompatible for X {
type Integer = usize;
fn into_integer(&self) -> Self::Integer {
self.0.len()
}
fn from_integer(n: Self::Integer) -> Self {
X::new(&std::iter::repeat("x").take(n).collect::<String>())
}
}
このような特殊例のみIntegerCompatible実装のマクロ化は無理ですね
let v :Vec<X> = range(X::new("xxx"), X::new("xxxxxxx")).collect();
println!("{:?}", v); // [X("xxx"), X("xxxx"), X("xxxxx"), X("xxxxxx")]
当然ですがX型についても動きました
128:デフォルトの名無しさん
21/11/24 18:18:05.49 AgL0hXz4.net
誰も見てねーから別のところでやれ
129:デフォルトの名無しさん
21/11/24 19:13:18.31 5wn/1hS5.net
話題がないんだもん
なんか話題を提供してくれ
130:デフォルトの名無しさん
21/11/24 23:19:00.01 e1u6MioL.net
結局Derefみたいにコンパイラが自動的に適用して変換してくれればそのintoやfromをプログラムには書かなくて済むんやろ
131:デフォルトの名無しさん
21/11/25 23:08:27.91 QVGqalzl.net
ファイルを開く操作って普通に考えたらFile::open_ほにゃらら()みたいなメソッドにOpenOptionsを渡すほうが自然だと思うんですが
OpenOptions::open()みたいな方法を取ってるのってどういう理由からなんでしょうか?
132:デフォルトの名無しさん
21/11/25 23:59:01.72 88pS2ZzI.net
>>129
・その方がメソッドチェーンで見やすい
・複雑な構造体を用意してそこに様々な値をセットして指定するインターフェースは非常に大変
・そのうえOS毎に違う部分もある
133:デフォルトの名無しさん
21/11/26 00:22:31.18 aH1+xhzE.net
>>129
Rustは関数定義でデフォルト値のあるオプション引数をサポートしてないから
オプション引数的な使い方をしたい場合はビルダーパターンを使うのが一般的
File::options().read(true).create(true).open("foo.txt”);みたいな使い方になる
134:デフォルトの名無しさん
21/11/26 09:37:38.31 kuMbCEJE.net
うーん
ダサいな
もっときれいにならんの?
135:デフォルトの名無しさん
21/11/26 09:50:01.95 kuMbCEJE.net
メソッドチェーンって過去の遺物だよね?
バグの温床だし
長いとどこかで消し忘れや二重指定が出て本人が気が付かなくなる
長くなっただけで破綻するんだからおかしい
136:デフォルトの名無しさん
21/11/26 10:00:30.74 5+U4u14D.net
>>133
一般的に今どきのプログラミング言語は全てメソッドチェーンが主流
もしかしてメソッドチェーンを使わない古い言語使いの方ですか?
137:デフォルトの名無しさん
21/11/26 10:06:22.27 yaO+xNFa.net
パイプライン演算子大好きとかLisp方面から来た人とかなのかも……
138:デフォルトの名無しさん
21/11/26 10:12:22.08 kuMbCEJE.net
え?メソッドチェーンってjqueryが流行ってたころの名残でしょ?
10年ぐらい前
メソッドチェーンなんて書いててだるいだけ
139:デフォルトの名無しさん
21/11/26 10:21:37.95 5+U4u14D.net
>>136
JavaScriptも今は関数型プログラミングが主流へと変わりメソッドチェーンだし外部ライブラリを使うインタフェースもメソッドチェーンがよく使われる
そしてRustも同様
どこの古い世界から来たお客さんですか?
140:デフォルトの名無しさん
21/11/26 10:34:43.95 E7I1X7f8.net
メソッドチェーンって関数型由来ではないし
141:デフォルトの名無しさん
21/11/26 11:18:59.37 /IsoxS9R.net
>>133
ビルダーは二重指定しても問題ない
消し忘れはどういう指定方法でも発生するからテストで防ぐ以外ないよ
テスト書かない文化の人?
142:デフォルトの名無しさん
21/11/26 11:23:55.78 /IsoxS9R.net
>>135
パイプ演算子も同じ問題抱えてるし
Lispでも今はthreadマクロで処理順に書く
143:デフォルトの名無しさん
21/11/26 11:31:53.80 oSCWFWAt.net
>>140
はえ~、そうか勉強になったわ
144:デフォルトの名無しさん
21/11/26 11:44:41.28 kuMbCEJE.net
>>139
二重指定は一方でtrue一方でfalse指定しているパターン
145:デフォルトの名無しさん
21/11/26 12:05:35.16 SqSfLhr2.net
>>132
どうやったら綺麗なのか参考のため教えて
Rustじゃなくて他の言語でも
仮想の言語でもいいよ
146:デフォルトの名無しさん
21/11/26 12:25:05
147:.02 ID:kuMbCEJE.net
148:デフォルトの名無しさん
21/11/26 12:29:19.27 kuMbCEJE.net
これが美しいと言う人はそれこそ逆ポーランド次元から来た異次元人だと思うよ
149:デフォルトの名無しさん
21/11/26 12:40:54.22 R0yJ4Kup.net
なかなか極端な例を出してきたな……
150:デフォルトの名無しさん
21/11/26 12:43:09.59 GoGODfBQ.net
おまいら日本語否定ですか。
>>144は設計が悪いんじゃない?
File::options().readwrite().newfile().open("foo.txt”);
なら自然だろ。
>>144
151:デフォルトの名無しさん
21/11/26 12:51:53.22 kuMbCEJE.net
逆ポーランド人現る
152:デフォルトの名無しさん
21/11/26 12:53:26.61 kuMbCEJE.net
ファイルを開こうと思うときにoptions型から思考がスタートする人は天才なんだろうな
それか飼いならされた人
153:デフォルトの名無しさん
21/11/26 12:57:20.12 kuMbCEJE.net
言語否定じゃなくてライブラリ設計がおかしい
実用無視の人が設計するとこうなる
154:デフォルトの名無しさん
21/11/26 12:58:29.05 Q6WyUjPa.net
File::open_with_options("foo.txt", &OpenOptions::new().read(true).create(true));
どっちが良いとか悪いとかじゃないと思うがなあ
155:デフォルトの名無しさん
21/11/26 13:13:18.47 kuMbCEJE.net
美しくないだろ普通に
156:デフォルトの名無しさん
21/11/26 13:13:45.37 HMe+psgI.net
OpenOptionsって名前で明らかに「ファイル開く時のオプションですよ」って名前なのにファイル開く操作まで持ってるから気持ち悪いんだよな
同じモジュールでディレクトリはDirBuilderとかあるんだから普通にFileBuilderとかにすりゃええやんとか思っちゃうけどなんか理由があったんかね
157:デフォルトの名無しさん
21/11/26 13:23:36.09 kuMbCEJE.net
確かに他だったらoptionのインスタンスを作ってopenに食わせる感じだな
それもどうかと思うけど
ほぼ定数みたいなものをわざわざ作って食わせるなんて
158:デフォルトの名無しさん
21/11/26 14:04:50.29 TIzT5fn9.net
慣れない人にとっては分かりにくいAPIにならざるを得ないのは確か
ただ他の言語でオプション引数やオーバーロードが2桁あるのが当たり前になってるようなライブラリを見るとそれぞれ一長一短あるなとは思う
159:デフォルトの名無しさん
21/11/26 14:15:29.08 Q6WyUjPa.net
>>154
associated constくらい用意してもらってもいいかもね
160:デフォルトの名無しさん
21/11/26 14:20:28.16 Ye0bskEh.net
>>153
OpenOptionsはrust1.0以前からあるものなのでその頃はビルダーのイディオムがなかったのだと思う
DirBuilderはrust1.6で追加されたものなので比較的新しい
161:デフォルトの名無しさん
21/11/26 14:57:24.65 AxmLr4ZJ.net
> 通常のCのオープン関数のほうが100倍キレイで簡潔で合理的
さっぱりわからんけどw
別に
File::options().readwrite().newfile().open("foo.txt”);
がいいとも悪いとも思わんし
cのopenがいいとも悪いとも思わん
どんな素晴らしいopen見せてくれるのかと思ったらガッカリした
162:デフォルトの名無しさん
21/11/26 15:03:20.08 BGloBCeB.net
OpenOptionsが分かりにくいってのはそのとおりだと思うし、実際みんなそう思ってるみたいで
ちょうどFile::optionsにするRFCが通るところだよ
163:デフォルトの名無しさん
21/11/26 16:18:02.52 Ye0bskEh.net
>>159
stabilizeのPRが10日前に取り込まれたから1月にはリリースされそう
164:デフォルトの名無しさん
21/11/26 18:16:06.66 kuMbCEJE.net
>>158
FILE * fopen(const char * filename, const char * mode);
こっちの方が100倍良い
165:デフォルトの名無しさん
21/11/26 19:18:40.42 wVYXipKz.net
>>161
それはfopenの仕様(引数の意味とか)を知っているのが前提だからなぁ。
filenameとmodeの順番を間違えたら読み間違える。
メソッドチェーンと比較するなら名前付き引数のメソッド呼び出しじゃない?
あるいはインテリセンス環境下という条件付きか。
166:デフォルトの名無しさん
21/11/26 19:21:46.75 Hq7eoo6P.net
>>161
ほんそれ
167:デフォルトの名無しさん
21/11/26 19:26:21.35 kuMbCEJE.net
仮想のコードで例が適切かどうかわからないけど
File::options().readwrite().newfile().read().open("foo.txt”)
と書いて直後で書き込めねえええええよと叫ぶよりfopenの方がいいだろ;
168:デフォルトの名無しさん
21/11/26 19:37:54.04 kuMbCEJE.net
あ、ミスった
それはさておき例のメソッドチェーン見ても瞬時に不安しかよぎらない
readwrite() が中で read(true).write(true) してるとして
readonly() が中でread(true)しかしてないんじゃないかとか不安
169:デフォルトの名無しさん
21/11/26 19:47:03.96 AxmLr4ZJ.net
>>162
> 名前付き引数のメソッド呼び出しじゃない?
俺も最初はそう言う話が出てくるのかとおもったら
cのopenを有りがたがってる奴が出てきて呆れた
170:デフォルトの名無しさん
21/11/26 20:09:04.42 XtGzaRsE.net
>>134
メソッドチェーンと関数チェーン|パイプラインが区別できてないんでは?
メソッドチェーンってレシーバの記述を省略できるとか文の数が減るとかでしかないでしょ。
171:デフォルトの名無しさん
21/11/26 20:37:35.34 kuMbCEJE.net
>>166
fopenとゴミみたいなメソッドチェーン
どっちがバグの温床だ?
172:デフォルトの名無しさん
21/11/26 20:42:02.78 kuMbCEJE.net
cのfopenはメソッドチェーンより簡潔だ
本当は他の言語のenumでいいんだけど
Open("rust.txt",FileMode.Open, FileAccess.Read)
これでバグは出ない
173:デフォルトの名無しさん
21/11/26 20:55:25.49 R0yJ4Kup.net
そんなに変わらへんやん、むしろuseで引き込む名前が増えて美しくないやん
174:デフォルトの名無しさん
21/11/26 21:02:27.39 Ye0bskEh.net
foo(true, false, true, false) や foo(None, None, None, Some(true)) みたいな呼び出しよりは100倍マシ
175:デフォルトの名無しさん
21/11/26 22:47:08.22 r6ugNRE0.net
ところでreadwrite()とかnewfile()ってなんですか?
176:デフォルトの名無しさん
21/11/27 00:09:21.90 riEP2Tv6.net
OpenOptionsの名称問題はともかく
なぜメソッドをチェーンさせているかというと理由は明白で
OpenOptionsExtトレイトをuseすると使えメソッドが拡張されて.mode()などが使えるようになるからだよ
177:デフォルトの名無しさん
21/11/27 00:15:29.99 4eTYjVC9.net
パイプ演算子は |> いつ実装されるの?
178:デフォルトの名無しさん
21/11/27 00:19:05.68 L+2AsAAt.net
どうせ上のtimes()トレイト君でしょ
179:デフォルトの名無しさん
21/11/27 00:42:49.16 w5eY7K13.net
チェーンの話なら、出来るだけチェーン派が多いけど.NETのLINQみたいになったら嫌?
それとも歓迎?LINQ形式のほうが分かりやすい。前の高階関数の話もそうだけど…
OpenOptionsはなんでもかんでもトレイトの弊害の気もする
articles =
from a in articles
orderby a.published descending
select new article_st;
URLリンク(github.com)
let e: Vec<i32> = linq!(from p in x.clone(), where p <= &5, orderby -p, select p * 2).collect();
このプロジェクトはcloneしてしまうところがダサいが、MSが参画しているということはありうるわけで
180:デフォルトの名無しさん
21/11/27 00:54:31.43 tWlgYd9Y.net
>>173
元がこうだもんな
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
そしてunix以外にも対応するために
std::os::unix::fs::OpenOptionsExtへ分けたのだろう
このメソッドチェーンビルダー方式以外のAPIでは詰む
181:デフォルトの名無しさん
21/11/27 05:50:15.37 IOkCeWcH.net
>>176
LINQはSQL風になる糖衣構文だからちょっと違うんちゃう。Rustに導入されても�
182:シの世界感と異質だし……。 個人的にはあれがSQL屋以外に直感的とは思えないw
183:デフォルトの名無しさん
21/11/27 09:55:26.18 sBo289Q7.net
>>174
rustは第一引数がselfの場合にメソッドのレシーバーになるのだから、実質パイプライン
184:デフォルトの名無しさん
21/11/27 11:01:40.71 kX7QbhiL.net
それはElixirと同じ「なんちゃってパイプライン」でメソッドチェーンが実現できるというだけでは。
ここで欲しいと言われているのは大元のF#のように自由関数やその部分適用が使えるパイプラインのことだろう。
185:デフォルトの名無しさん
21/11/27 12:26:31.88 Xdw8IP3R.net
自由関数?
186:デフォルトの名無しさん
21/11/27 14:14:35.41 A1VfIYPt.net
第一引数がselfとかじゃないやつのことじゃね
187:デフォルトの名無しさん
21/11/27 15:58:50.40 lc2cVbH3.net
自由関数ってなんだっけ?
自由変数ならわかるんだけど
188:デフォルトの名無しさん
21/11/27 15:59:45.03 Xdw8IP3R.net
定義を示してほしいわな
F#界隈にもRust界隈にも無い用語だし
189:デフォルトの名無しさん
21/11/27 16:36:46.11 kX7QbhiL.net
何気なく使ってたけど調べてみたらC++用語だったか。しかも日本じゃ「フリー関数」という表記の方が一般的なんだな。
メンバー関数(メソッド)ではない素の関数ということで。
190:デフォルトの名無しさん
21/11/27 17:08:07.51 FtAU5QYE.net
その意味ならElixirは自由関数も部分適用も使えると思うが・・・
191:デフォルトの名無しさん
21/11/27 17:20:05.91 kX7QbhiL.net
そっちか。
Elixirのパイプラインの使用例でよく見る形式が部分適用とは違うんで使えないものと思っていた。すまん。
部分適用は別の表記になるんだな。
192:デフォルトの名無しさん
21/11/27 17:59:12.10 g2vJAoph.net
C++でもフリー関数と言われたら free() のこと思い浮かべそうだが
193:デフォルトの名無しさん
21/11/27 18:21:56.04 molZfSEM.net
>>178
そうは言っても現状の下のこれが直感的に分かり易いとは思えないけど、、LINQはSQLとは関係ないよ。
whereとかselectがそれに見えるけど、言語的には直行性を高めた統合クエリなだけ
let mut y: Vec<i32> = x.clone().filter(|p| p <= &5).collect();
y.sort_by_key(|t| -t);
let y: Vec<i32> = y.into_iter().map(|t| t * 2).collect();
194:デフォルトの名無しさん
21/11/27 19:30:03.08 tWlgYd9Y.net
>>189
どこが見にくいのか具体的に教えて
use itertools::Itertools;
let y: Vec<i32> = x
.filter(|p| p <= &5)
.sorted_by_key(|t| -t)
.map(|t| t * 2)
.collect();
195:デフォルトの名無しさん
21/11/27 21:12:33.33 VfaT4D+K.net
>>185
> 何気なく使ってたけど調べてみたらC++用語だったか
どこの世界に非メンバ関数をフリー関数という馬鹿がいるのか詳しく
CかC++の規格のどこを参照すれば定義されているのか詳しく
196:デフォルトの名無しさん
21/11/27 21:19:49.42 w2+KtZN6.net
江添の馬鹿が言ってた。
URLリンク(cpplover.blogspot.com)
197:デフォルトの名無しさん
21/11/27 21:31:03.67 w2+KtZN6.net
Herb Sutterという馬鹿も使ってるな。
URLリンク(www.open-std.org)
198:デフォルトの名無しさん
21/11/27 21:39:57.32 VfaT4D+K.net
規格の上ではまだ見つけられないが
URLリンク(timsong-cpp.github.io)
member function
non-member function
という当然の名称が出てきている
日本語版の規格分かる人「フリー関数」とやらを引用してくだしあ
199:デフォルトの名無しさん
21/11/27 21:43:56.78 w2+KtZN6.net
規格で定義された用語というより業界用語的なもんでないの
URLリンク(github.com)
200:デフォルトの名無しさん
21/11/27 21:44:30.32 lc2cVbH3.net
馬鹿っていちいち言うなよ
素直になれや
201:デフォルトの名無しさん
21/11/27 22:07:33.35 tWlgYd9Y.net
その「フリー関数」とはクラスのメンバー関数ではない非メンバー関数として
クラスのないRustではその非メンバー関数の定義はどうなるの?
例えば以下のprint_all()は適当に作ったトレイトPrintAllのメンバーかもしれないけど
現実には('a'..='z').print_all();が動作してしまうわけで『誰のメンバー関数』なの?それとも非メンバー関数?
trait PrintAll<T> {
fn print_all(self);
}
impl<I: Iterator<Item=T>, T: Display> PrintAll<T> for I {
fn print_all(self: I) {
self.for_each(|x| println!("{}", x));
}
}
202:デフォルトの名無しさん
21/11/27 22:20:27.30 w2+KtZN6.net
第一引数がselfである関数(メソッド)を自由関数として呼び出すことはできるけどその逆は真ではないってことじゃないかな。
URLリンク(en.wikipedia.org)
203:デフォルトの名無しさん
21/11/27 22:24:30.93 dpgg2nfE.net
メソッド呼び出しできない関数は「フリー関数」ってことでええんちゃう?
非メンバ関数って呼ぶほうが断然一般的だとは思うが
204:デフォルトの名無しさん
21/11/27 22:47:33.66 riEP2Tv6.net
>>197
selfがIteratorだからクラス志向だとprint_all()はIteratorクラスのメンバーかな
しかしそのPrintAllはIteratorを継承してないから難しい
205:デフォルトの名無しさん
21/11/27 23:15:00.42 udgmz45E.net
URLリンク(dev.to)
この手のPhantomDataとジェネリクス使って特定の条件満たした状態じゃないとbuildとかのメソッドが実装されないようにしたりしたビルダーパターンってここの人たちはどう思ってる?
説明だけ見るとめっちゃ良いやん!って思うけどいざ自分で書くとメリットに対して無駄に複雑&使う側としてもエラーが見にくいとかなんか微妙に感じちゃう
206:デフォルトの名無しさん
21/11/28 00:27:08.53 j8Nrs0jp.net
>>200
例えばこのように任意の実装が出来るからprint_all()はIteratorのメンバーではなくあくまでもPrintAllのメンバー
struct V<T>(Vec<T>);
impl<T: Display> PrintAll<T> for V<T> {
fn print_all(self) {
self.0.into_iter().for_each(|x| println!("{}", x));
}
}
207:デフォルトの名無しさん
21/11/28 00:30:34.72 Fw4ypgsa.net
重要なのはフリー関数の定義じゃなくてパイプライン演算子の適用範囲だぞ
208:デフォルトの名無しさん
21/11/28 00:54:44.73 j8Nrs0jp.net
>>201
長くて斜め読みしかしていないがもっとわかりやすく示すとこういうことか
まず状態を示すダミーな型を作っておく
trait State {}
impl State for ToDo {}
impl State for Done {}
#[derive(Debug)]
struct ToDo;
#[derive(Debug)]
struct Done;
次にビルダーの構造体にダミーな型も収容する (サイズはゼロ)
type PD<T> = std::marker::PhantomData<T>;
#[derive(Debug)]
struct Builder<SetA: State, SetB: State> {
a: i32,
b: i32,
_a: PD<SetA>,
_b: PD<SetB>,
}
つまり変数aがセットされたか否かの状態をダミーな_aの型で示す
(つづく)
209:デフォルトの名無しさん
21/11/28 00:58:02.64 j8Nrs0jp.net
>>204の続き
初期値ToDoで開始してセットされたらDoneに変える
impl<SetA: State, SetB: State> Builder<SetA, SetB> {
fn new() -> Builder<ToDo, ToDo> {
Builder { a: 0, b: 0, _a: PD {}, _b: PD {}, }
}
fn set_a(self, a: i32) -> Builder<Done, SetB> {
Builder { a, b: self.b, _a: PD {}, _b: PD {}, }
}
fn set_b(self, b: i32) -> Builder<SetA, Done> {
Builder { a: self.a, b, _a: PD {}, _b: PD {}, }
}
}
全部がDoneになった時だけ実行可能にしておく
impl Builder<Done, Done> {
fn execute(&self) {
println!("OK: {:?}", self);
}
}
最初の呼び出しをわかりやすく用
fn new_builder() -> Builder<ToDo, ToDo> {
Builder::<ToDo, ToDo>::new()
}
あとは両方がセットされると実行できる
fn main() {
new_builder().set_a(123).set_b(456).execute();
}
片方でもセットを忘れるとコンパイル時にexecute()が解決できず失敗する
自分でこのコードを毎回間違えずに書くのは面倒なのでマクロ化されるなら採用
210:デフォルトの名無しさん
21/11/28 01:06:31.53 eXqoW6w6.net
またチラ裏コードの垂れ流し
いい加減やめてほしい
211:デフォルトの名無しさん
21/11/28 02:33:07.17 ZOlCZyFx.net
>>201
こういうのは手書きするのではなくて derive で良い感じに実装してほしい
あとrustdocの出力がごちゃつきそうなのが気になる
212:デフォルトの名無しさん
21/11/28 02:38:47.39 D4pWSHhU.net
自己満足コード見せられても困るよな
213:デフォルトの名無しさん
21/11/28 02:48:11.47 Fw4ypgsa.net
config部分が静的に決まってるならいいけど
UIやコマンドラインから動的にconfigしたい場合Builder traitを用意してtrait objectとして持ち回して
.execute()するためにdowncastする必要が……とかで二度手間になりそう
214:デフォルトの名無しさん
21/11/28 02:53:52.09 ghhRE59c.net
>>201のコードがいいと感じる感性が理解できないよ
215:デフォルトの名無しさん
21/11/28 10:39:31.01 XbegH2kB.net
>>201
難しい話をすると
ビルダはディレクタに対して抽象化されており
一個のディレクタが複数のビルダをケアできることを考えると
必須パラメータというのをビルダ実装ごとに準備するなら
ビルダというインタフェースがディレクタに対して実質破綻してると思う
でも
ビルダインタフェースが正しい呼び出し順を想定してたりするのをアリとするなら
必須パラメータも同じように勝手に想定しておいて
使う側にはドキュメントなりなんなりで勝手に指示だしとけば十分とも思う
そんで
呼び出し順や必須か否かに想定を置きたくない
呼び出し側に完全な自由度を与えたビルダインタフェースを提供したいなら
インタフェース Fooビルダ {Fooビルダ a(); Fooビルダ b();}
クラス 実際のビルダ implements Fooビルダ {
Foo(必須な何か x) {略} // コンストラクタで与えるとか
Fooビルダ 必須なc() {略} /* 実際のビルダ固有のメソッドとして与えて
new 実際のビルダ().必須なc().a() などと呼ぶか
Fooビルダ a() {略}
Fooビルダ b() {略}
Foo create() {略}
}
のようにして、ビルダインタフェース側だけはクリーンに守っておけばスッキリかも?
元のURLにあるように何が必須かをパラメータ化したいという欲求は解消してないのは認める
216:デフォルトの名無しさん
21/11/28 11:48:41.93 sDAG0wCq.net
インタフェースとかクラスとか意味不明すぎ
217:デフォルトの名無しさん
21/11/28 23:35:20.49 Fzo1fdIE.net
ここまで意味不明な文章書けるのって逆に凄いよ
このレベルは久々に見たわ
218:デフォルトの名無しさん
21/11/29 01:53:08.06 zo5XubVi.net
Javaコードの識別子の部分を日本語で書いて
GoFのオリジナルのBuilderパターンを説明しただけっぽい
なんでRustスレでそんな話をしたのかだけは完全に謎だが……
219:デフォルトの名無しさん
21/11/29 02:06:35.83 27e/xIh/.net
普通のBuilderであまり困ったことないからなあ
操作によって遷移していく状態があって、状態ごとに可能な操作が違う(呼び出せるメソッドが違う)とかなら意味あると思う(Socketのbind->accept->read/writeみたいな)
ただこういうものはもはやBuilderと呼ぶべきものではないと思う
220:デフォルトの名無しさん
21/11/29 09:45:31.17 3xWgo/Oc.net
それはStateパターンでしょ
221:デフォルトの名無しさん
21/11/29 15:25:28.77 aqqw7fQi.net
URLリンク(play.rust-lang.org)
sもmutにしてDerefMutもimplしてるのになんでgの方は通らないんでしょうか?
222:デフォルトの名無しさん
21/11/29 16:49:12.44 4MgUQE5v.net
>>217
もっと単純に let z: *mut i8 = *s; も通らないので
これはDerefMutの条件であるmutable contextを満たしてないのではないか
そしてもちろん let z: &mut i8 = *s; は通るし **s = 88; も通るからDerefMut自体は機能している
223:デフォルトの名無しさん
21/11/29 18:03:30.09 3r4jR24z.net
PR出てるけどどうなるかわからない
URLリンク(github.com)
現状は
let s = &mut *s;
g(*s);
するか
g(&mut **s);
するか
224:デフォルトの名無しさん
21/11/29 18:58:43.82 Y4F1AeLY.net
>>219
おお、そのものズバリが
あざます!
225:デフォルトの名無しさん
21/11/30 18:31:42.77 8WvE/rry.net
ifの閉じカッコにセミコロンが必要となる条件を教えてください
以下のプログラムはそれが足りないと指摘されてコンパイルエラーとなり
セミコロンを付けると通って動作するのですがどういう原理なのでしょうか?
fn main() {
for line in BufReader::new(io::stdin()).lines() {
let line = line.unwrap();
if let [first, second, rest] = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>()[..] {
println!("{}:{}:{}", first, second, rest);
}
}
}
226:デフォルトの名無しさん
21/11/30 18:58:54.86 ZW4IpnTF.net
戻り値?
227:デフォルトの名無しさん
21/11/30 20:27:35.13 s7fhQ2Tk.net
ブロック式最後の式の値は、ブロック式の値として返るので drop されるタイミングがブロックの末尾よりも後の箇所になるということかな
for のブロックは値を返さないけど、ブロック式と同じ扱いをされているっぽい
URLリンク(play.rust-lang.org)
228:デフォルトの名無しさん
21/11/30 21:12:32.45 8WvE/rry.net
>>223
>> ブロック式最後の式の値は、ブロック式の値として返るので
もちろんおっしゃる通りでその例でも>>221の例でもif式の値は明瞭に()ですね
>> drop されるタイミングがブロックの末尾よりも後の箇所になるということかな
ブロック式の値として返るのは()ですから変数lineのdropタイミングが後にはならないように思うのですがどうなのでしょう?
229:デフォルトの名無しさん
21/11/30 21:32:53.10 hx6pGpzB.net
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
↑エラーメッセージに理由書いてるよ
230:デフォルトの名無しさん
21/11/30 21:55:20.40 8WvE/rry.net
>>225
そのエラーメッセージでは理由になっていないのではないでしょうか?
例えば(for文を使わない)>>223の以下の部分を
if let [_first, _second, _rest] = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>()[..] {}
このように2行へ書き換えても同じくセミコロンを付けろエラーとなりますが
let x = line.splitn(3, ' ');
if let [_first, _second, _rest] = x.collect::<ArrayVec<_, 3>>()[..] {}
このように2行へ書き換えるとコンパイルが通ります
let x = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>();
if let [_first, _second, _rest] = x[..] {}
この差をどう見るのか教えていただけますでしょうか
231:デフォルトの名無しさん
21/11/30 22:02:05.25 s7fhQ2Tk.net
>>224
・lineの寿命はif-let式の寿命と一致する
・ブロックの最後の文の寿命はブロックを囲む文の寿命と一致する
という仕様になっていると思われる
型は関係なくてブロック内の最後の式か否かが重要
232:デフォルトの名無しさん
21/11/30 22:26:55.08 hx6pGpzB.net
>>226
>このように2行へ書き換えるとコンパイルが通ります
>let x = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>();
line.splitnでborrowしたものを使ってるtemporaryは
let xで受けたタイミングでtemporaryじゃなくなる
(エラーメッセージにあるdropped soonerの状態になってる)
if式がブロックの最後の式でそのif式の一部がborrowを使ってるtemporary
セミコロンがないとtemporaryが解放される前にローカル変数のlineが解放されるからライフタイムのエラー
そのうち修正されるかもしれないけど今はそういう動きってこと
233:デフォルトの名無しさん
21/11/30 22:31:28.50 hx6pGpzB.net
>>226
>let x = line.splitn(3, ' ');
>if let [_first, _second, _rest] = x.collect::<ArrayVec<_, 3>>()[..] {}
234:デフォルトの名無しさん
21/11/30 22:35:06.87 hx6pGpzB.net
すまん、投稿ミス。
値を返したいときはセミコロンを追加するだけじゃだめだから
一旦変数で受けてからその変数をブロックの最後に書く
235:デフォルトの名無しさん
21/11/30 22:39:09.24 8WvE/rry.net
ふむむ
lineがヒープにあると以下の2行だけでセミコロン付けろエラーになりますね
let line = String::from("A B C D E");
if let [_first, _second, _rest] = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>()[..] {}
このif let文を例えば以下のように変えるとセミコロンを付けなくても通るのはどうしてでしょう?
if let Some(_s) = line.get(3..6) {}
236:デフォルトの名無しさん
21/11/30 22:41:34.40 rIKeeiBO.net
なんでセミコロン付けると一時オブジェクトの寿命変わるんだって思ったけどそういえばC++にもそんな仕様あったな……
完全式じゃなくて戻り値に使うから破棄されないってことか
237:デフォルトの名無しさん
21/11/30 23:55:05.80 Y6JwF3m3.net
>>230
ブロックの最後で返す値は変数に入れなくても複雑な式でも大丈夫ですよ
238:デフォルトの名無しさん
21/12/01 12:41:12.28 KNT1MQhQ.net
なんでBufReadトレイトはあるのにBufWriteトレイトは無いんですか?
239:デフォルトの名無しさん
21/12/01 13:25:08.66 2cjkBPbo.net
>>231
詳しく知りたければまずはここ↓だけど
URLリンク(doc.rust-lang.org)
言語自体をいじりたいわけじゃなければ
あんまり深追いしても役に立たないよ
240:デフォルトの名無しさん
21/12/01 16:06:22.55 oI4zTDt2.net
>>234
BufReadはReadに対して追加の機能(行単位の読み込みなど)があるけど
Writeに対して追加すべき機能がないからBufWriteは存在しないのではないかと思われる
241:デフォルトの名無しさん
21/12/01 21:58:24.00 QwFnQ8Qa.net
flushしなけりゃ下のレイヤのどこかで勝手にバッファリングされることがほとんどだから用意する意味がないんだろな。
242:デフォルトの名無しさん
21/12/01 23:34:44.63 hYYowF9a.net
>>237
いやBufWriteトレイトが存在しないだけであってBufWriterはちゃんとある
そしてBufWriterがバッファリングする最後の要
誰かが勝手にバッファリングしてくれることはない
例えば何行もファイルに書き込む時にBufWriter使わずに各行毎にwriteしてたら遅くなる
243:デフォルトの名無しさん
21/12/03 14:04:46.92 xjf4hyRh.net
URLリンク(crates.io)
とか
URLリンク(crates.io)
みたいなの使ってる人おる?
これ使うくらいならLL使うとは思うが
244:デフォルトの名無しさん
21/12/03 14:45:17.46 RidNMi7I.net
REPL的にコードスニペットの実行確認するために使ったりはできるのかな
今はplaygroundで事足りているが
245:デフォルトの名無しさん
21/12/04 15:22:12.31 f29/w5s1.net
Read::read_to_end()に空のVecを渡した時に戻り値のusizeとVecのlen()が違う値になる事って有り得ますか?
246:デフォルトの名無しさん
21/12/04 23:32:14.41 VZLpyp2B.net
同じと思う
let file_size = file.metadata().map(|m| m.len())?;
let file_pos_before = file.stream_position()?;
let read_buf_size_before = read_buf.len();
このようなfileとread_bufがある任意の状況で
let read_size = file.read_to_end(&mut read_buf)?;
とread_to_end()すると以下が常に成り立っていると思われる
assert_eq!(read_buf_size_before + read_size, read_buf.len());
assert_eq!(file_pos_before + read_size as u64, file_size);
247:デフォルトの名無しさん
21/12/05 23:43:02.39 +2NbegRW.net
> loop式はbreakで指定した値を返せるのに
> なぜwhile式やfor式は値を返せないの?
> Option型にしてbreakで値を指定した時だけSome(値)としてそれ以外はNoneとすれば便利なのに
そのloopと組み合わせればよい
(例)
let r = 'result: loop {
for x in 1..100 {
for y in 1..100 {
if x * y > 1234 {
break 'result Some((x, y));
}
}
}
break 'result None;
};
assert_eq!(r, Some((13, 95)));
248:デフォルトの名無しさん
21/12/06 12:54:15.14 BduPW1Ae.net
>>243
forよりイテレータを繋げた方が単純でわかりやすいよ!と言おうとしたら
let r = (1..100)
.map(|x| (1..100)
.map(|y| (x, y))
.find(|(x, y)| x * y > 1234)
)
.find(|o| o.is_some())
.map(|oo| oo.unwrap());
assert_eq!(r, Some((13, 95)));
むしろ複雑でわかりにくくなってしまったw
まさかの>>243のダミーloop使用がベストアンサーなのか!?
249:デフォルトの名無しさん
21/12/06 13:12:38.43 PE/XVSQC.net
>>244
flat_mapを使えば良い
let r = (1..100).flat_map(¦x¦ (1..100).map(¦y¦ (x, y))).find(¦(x, y)¦ x * y > 1234);
250:デフォルトの名無しさん
21/12/06 13:28:57.99 GjZweGXf.net
どれもこれも分かりやすいとは思えない
251:デフォルトの名無しさん
21/12/06 15:59:22.06 BduPW1Ae.net
結局見やすくこう書けるといいんだよね
let r = pair(1..100, 1..100).find(|(x, y)| x * y > 1234);
assert_eq!(r, Some((13, 95)));
これでassertも通ったけどyのイテレータとx自身の2ヶ所にCloneが必要となってしまった
避けられないような気がするけどどうでしょうか?
fn pair<IX: Iterator<Item=X>, IY: Iterator<Item=Y> + Clone, X, Y>(ix: IX, iy: IY) -> Pair<IX, IY, X, Y> {
Pair { cur_ox: None, cur_iy: iy.clone(), ix: ix, iy: iy, }
}
struct Pair<IX: Iterator<Item=X>, IY: Iterator<Item=Y>, X, Y> {
cur_ox: Option<X>, cur_iy: IY, ix: IX, iy: IY,
}
impl<IX: Iterator<Item=X>, IY: Iterator<Item=Y> + Clone, X: Clone, Y> Iterator for Pair<IX, IY, X, Y> {
type Item = (X, Y);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let None = self.cur_ox {
self.cur_ox = self.ix.next();
}
if let Some(ref x) = self.cur_ox {
if let Some(y) = self.cur_iy.next() {
break Some((x.clone(), y));
} else {
self.cur_ox = None;
self.cur_iy = self.iy.clone();
continue;
}
} else {
break None;
}
}
}
}
252:デフォルトの名無しさん
21/12/06 16:01:51.04 NQsvo9rr.net
最近知ったボクの大発見書いていい?
let a: Option<i32> = None;
これは
let a = None::<i32>;
と書ける
関数のパラメータとして渡そうとして
f(None)で怒られたとき
f(None::<i32>)として怒られない
しょうもないレス失礼いたしました
253:デフォルトの名無しさん
21/12/06 16:21:14.38 BduPW1Ae.net
>>248
曖昧性を確定させる::<型>の指定は色々なところで出てくるね
例えばcollect::<Vec<_>>()とか
ブロックやクロージャの返り値でOk::<(),std::io::Error>(())とか
254:デフォルトの名無しさん
21/12/06 16:35:36.71 McsJgKJD.net
>>247
itertoolsのcartesian_productがほぼそれ
255:デフォルトの名無しさん
21/12/06 17:07:16.47 +ZC47hZJ.net
iproduct!
256:デフォルトの名無しさん
21/12/06 17:09:45.91 Fu08U5Ef.net
>>248
型パラメータは Option のパラメータなので順当に考えれば Option::<i32>::None と書くべきだし実際にそれで通るんだけども、
歴史的経緯でバリアントにも付けられるようになってる。
短く書けるから習慣的にはバリアントに型を付けるほうが多いかな……?
257:デフォルトの名無しさん
21/12/06 17:31:38.25 BduPW1Ae.net
>>250
なるほど直積集合かぁ
同じくyのイテレータとxはCloneを要求してますね
fn cartesian_product<J>(self, other: J) -> Producfgt<Self, J::IntoIter>
where
Self: Sized,
Self::Item: Clone,
J: IntoIterator,
J::IntoIter: Clone,
258:デフォルトの名無しさん
21/12/06 20:06:32.13 NQsvo9rr.net
>>249
> Ok::<(),std::io::Error>(())とか
正直見たことなかったです
>>252
> 歴史的経緯でバリアントにも付けられるようになってる。
(´・∀・`)ヘー
そういうわけなんですね
259:デフォルトの名無しさん
21/12/06 21:47:08.33 BduPW1Ae.net
>>254
例えばlet x = spawn(async { ... });して裏で何か処理をやらせといた結果を
後でもしエラーが出ていたら進めちゃいけないタイミングでx.await?;で確認するわけだけど
spawnが返す型をxに律儀に記述するのは面倒なのでそこは略すとして
asyncブロック内で?とOk(())だけ書くとコンパイラが文句を言うので仕方なく記載
260:デフォルトの名無しさん
21/12/06 22:11:44.75 NQsvo9rr.net
なるほど勉強になりました
261:デフォルトの名無しさん
21/12/07 21:26:04.18 pFZAiCY5.net
12次元までならiproduct!が使える
use itertools::iproduct;
let r = iproduct!(1..100, 1..100).find(|(x, y)| x * y > 1234);
let r = (|| {
for (x, y) in iproduct!(1..100, 1..100) {
if x * y > 1234 {
return Some((x, y));
}
}
return None;
})();
262:デフォルトの名無しさん
21/12/08 14:18:48.01 6NuoEm2L.net
ARM版のWindowsでRustのコードを書くのってめんどくさいな
生のWindowsで使用する場合、VisualStudioに付属するx86用のリンカーが非推奨警告を無視すれば使えたものの、次期バージョンからx64専用になってしまうっぽい
一方、仮想環境等+VSCodeでは、RustAnalyzer等が機能せず苦労する・・・・
持ち運び用にケチってMacを買わなかったのが大問題だった、なんかいい方法ないのかな?そもそもARM Windowsで動くリンカーってVisualStudio付属のものしかないのかな?
ケチったって言ってもよくよく考えてみると大して金が浮いてもないし、変なもん買っちまった
263:デフォルトの名無しさん
21/12/08 14:36:22.94 W5vI+s6z.net
>>258
i686ターゲットのバイナリをバイナリ変換で動かしてるってこと?
aarch64-windowsターゲットとARM用のリンカ使えばネイティブのARMバイナリできるんじゃないかと思うけど
264:デフォルトの名無しさん
21/12/08 20:15:10.58 pzF9gjPk.net
バイナリバイナリルルルルルー。
265:デフォルトの名無しさん
21/12/08 21:25:01.32 t4Pzut9P.net
rustはどんどん新しい機能が追加されていくのはいいけど、
後方互換性を気にして過去に追加された「間違った」機能を削除するという
思い切ったこともやって欲しいな
これまでの言語は後方互換性にとらわれて滅茶苦茶になってるから
266:デフォルトの名無しさん
21/12/08 21:29:04.83 ff6DaDGr.net
>>261
C++かな?
267:デフォルトの名無しさん
21/12/08 21:44:15.05 tBq4QMAR.net
>>261
editionでは物足りない?
268:デフォルトの名無しさん
21/12/08 21:50:49.56 t8qOOWvR.net
実際2021editionではレンジパターンの ... が削除されて ..= に一本化された
269:デフォルトの名無しさん
21/12/09 08:53:14.94 sqaPNXyj.net
>>258
今どきDocker使うのはデフォだから何の問題もない、因みにRustAnalyzerも使える
使えないのは、復数のプロジェクトが見える状態にしてるから
今どき復数ウィンドウ立ち上げても何の問題もない、ルートフォルダを固有にしたら解決
つまり、調査不足が原因
270:デフォルトの名無しさん
21/12/09 10:59:17.60 4q0mFQ+L.net
ARM は安物のイメージがある。
WSL2, Linux, Docker, VSCode とか使えるのかな?
Mac も、M1 に変わったから
271:デフォルトの名無しさん
21/12/09 11:10:00.09 XwSSuf4e.net
原理的には ARM �
272:フほうがインテル系 (もはや AMD 系と呼ぶべきか) よりも高速化できる可能性があるとは言われている。 今以上に回路を細かくするのは無理というところまできてしまっているのでアーキテクチャのほうで見直しが必要なんだが インテル系は互換性が足かせになってしまっていてあまり思い切ったことが出来ない。 現時点はインテル系向けにチューニングされたソフトウェア資産が多いから上手くいっているけど将来もそうとは限らない。
273:デフォルトの名無しさん
21/12/09 11:45:18.26 t4DQqTrM.net
>>267
スレチだけどその話の出展あったら教えてほしい
274:デフォルトの名無しさん
21/12/09 14:22:31.39 RSXecyhf.net
CISCRISCの話じゃなくてメモリモデルの話ならそうかなって思う
今のx64って実質中身RISCって聞いたことあるし
Rustで言うとx64では全部SeqCstとして扱われるみたいな話
275:デフォルトの名無しさん
21/12/09 17:42:03.96 VJ9QB09P.net
リソースの話だけなら命令デコーダーとか?
x86_64は拡張や互換性とか可変長命令だったりで無駄にトランジスタ消費するのが電力性能比的に足かせみたいな
276:デフォルトの名無しさん
21/12/09 20:39:08.00 0MvTGuxY.net
今どきのプロセッサだと分岐予測器やキャッシュが支配的でデコーダなんて誤差だと思う
あと可変長は命令側の帯域やキャッシュ効率が良くなるという面もあってRISC系でも採用してることが多い
結局両者とも長年の改善で似たようなとこに落ち着いてるわけで、ISAが違うからどうこうみたいな話は結構眉唾
277:デフォルトの名無しさん
21/12/11 19:08:58.23 oic9EtmK.net
こういうことをしたいのですがコンパイルエラーとなってしまいます
const DEFAULT_NAME: &str = "namae";
let arg1: Option<String> = std::env::args().nth(1);
let name: &str = arg1.map_or(DEFAULT_NAME, |s| s.as_str());
どう直せばよいでしょうか?
278:デフォルトの名無しさん
21/12/11 19:10:55.89 ZSpAs+oG.net
namaeじゃなくてnameな
aが余計
この程度の英語のスペル書けないとかプログラミング向いていないしやめた方がいいよ
279:デフォルトの名無しさん
21/12/11 19:21:25.94 UNEoSQah.net
自演ボケか?
280:デフォルトの名無しさん
21/12/11 19:35:01.57 K+rsGRUk.net
>>272
そのままだとarg1が消費されてなくなるのに参照だけ残ることになるからエラー
Stringで受ければいいよ
let name: String = arg1.unwrap_or(DEFAULT_NAME.to_string());
281:デフォルトの名無しさん
21/12/11 20:19:05.99 XRkKLs6o.net
>>273
すげーな。してはいけないレビューの典型例じゃねーか。
すげーな
282:デフォルトの名無しさん
21/12/11 20:24:40.06 Z1L5tslT.net
いやネタでしょ
283:デフォルトの名無しさん
21/12/11 20:29:51.18 /anFx7me.net
>>275
これだとarg1がSomeの時もto_string()が呼び出されて無駄なヒープアロケーションが走るのでunwrap_or_elseにすべき
284:デフォルトの名無しさん
21/12/11 22:35:07.46 oic9EtmK.net
みなさんありがとうございます
Stringにする方法は無事にこれで動きました
let name: String = arg1.unwrap_or_else(|| DEFAULT_NAME.to_string());
元の質問>>272のように&strにする方法は無いのでしょうか?
もし可能ならばto_string()のヒープアロケーションを減らせるかなという質問です
285:デフォルトの名無しさん
21/12/11 22:59:59.99 yVS9OnV5.net
>>279
Cowかな
let name: Cow<str> = arg1.map_or(Cow::Borrowed(DEFAULT_NAME), |s| Cow::Owned(s));
286:デフォルトの名無しさん
21/12/11 23:20:49.43 yVS9OnV5.net
場合によってはこれでもいいのかな?
let name: &str = &arg1.as_ref().map_or(DEFAULT_NAME, |s| s.as_str());
287:デフォルトの名無しさん
21/12/11 23:54:40.62 tYxQqCnY.net
>>281
それでもよいけど正解はシンプルなこれ
let name: &str = if let Some(ref arg1) = arg1 { arg1 } else { DEFAULT_NAME };
まずarg1を消費しないようにrefで受ける
2代目のarg1は&Stringなので自動的に&strへderefされる
288:デフォルトの名無しさん
21/12/12 02:10:02.51 h/Sb7JBW.net
1.40からas_derefっつうのがあるんだってさ
let name = arg1.as_deref().unwrap_or(DEFAULT_NAME);
でもコマンドライン引数の場合は消費しないメリットがほぼ無いのでCowのほうがいいかな
289:デフォルトの名無しさん
21/12/12 04:28:54.46 8d+idsXS.net
どの型で統一すべきかは
(1) その後に加工伸長などするならString (arg1をここで&strに統一するのは無駄)
(2) 参照するのみなら&str (DEFAULT_NAMEをここでto_stringするのは無駄)
(3) その後に判明する条件次第で両ケースありうるならCow (ただし常にCow利用はCowコストが無駄)
って感じ?
290:デフォルトの名無しさん
21/12/12 10:07:32.08 svMJrknn.net
おそらくその通りだけど、CLIツールの初回一回だけのアロケーションにそこまでこだわるのがそもそも無駄って気もする
ループ内とかでもなければ雑にString作っちゃっていいかもね
291:デフォルトの名無しさん
21/12/12 10:31:14.68 eE6Pv/WZ.net
んだ
292:デフォルトの名無しさん
21/12/12 10:46:59.45 8d+idsXS.net
>>285
今回のケースはそうだね
ただしヒープ割り当てをなるべく避ける様々な手法を把握しているか否かは色んな局面で効いてくるから
今回6通りも動くコード例が示されたことは多様に対応可能な柔軟性の良さかな
気にしなくても書けるし気にすれば効率を上げることができる点で
293:デフォルトの名無しさん
21/12/12 18:38:25.33 h/Sb7JBW.net
>>284
Cowと&strに揃える場合の一番の違いはライフタイム管理
Stringを&strにするとライフタイム管理がつきまとうから
すぐ使いきる場合以外はCowに比べてメンテナンスしにくいコードになる
294:デフォルトの名無しさん
21/12/12 18:59:43.58 3rjDzGgS.net
文字列についてはStringにするかCow<str>にするか迷うくらいならinternしちゃうのも手かと
どのライブラリが定番なのかよく知らないけど
295:デフォルトの名無しさん
21/12/12 19:49:57.76 be4Z/veb.net
>>281
> let name: &str = &arg1.as_ref().map_or(DEFAULT_NAME, |s| s.as_str());
それarg1の前の&は不要でこれで動く
let name: &str = arg1.as_ref().map_or(DEFAULT_NAME, |s| s.as_str());
さらにas_str()使うより短く書けて&**sで&strになる
let name: &str = arg1.as_ref().map_or(DEFAULT_NAME, |s| &**s);
さらに&Stringのsのままでもderefされるため大丈夫
let name: &str = arg1.as_ref().map_or(DEFAULT_NAME, |s| s);
クロージャが何もしてないからといって無くしてしまうとderefが効かず型不一致コンパイルエラー
× let name: &str = arg1.as_ref().unwrap_or(DEFAULT_NAME);
そこで明示的にderefしてやればよい
let name: &str = arg1.as_deref().unwrap_or(DEFAULT_NAME);
296:デフォルトの名無しさん
21/12/13 21:23:25.03 zBnuOauJ.net
ken okabeのqiitaの記事がまた炎上してるよ
mod_poppoにボコボコにされてる
297:デフォルトの名無しさん
21/12/13 22:30:34.09 i33Tname.net
あれ、Qiitaには垢バンされて投稿できないんじゃなかった?
298:デフォルトの名無しさん
21/12/13 23:00:30.90 Yx06Lw1d.net
ググってもよくわからないのですが、どういった方なんですか?
299:デフォルトの名無しさん
21/12/13 23:30:17.26 IeJGNs4K.net
盛大な時間の無駄になるだけなので調べてはいけない
300:デフォルトの名無しさん
21/12/13 23:59:51.20 mqpFvLOG.net
>>291
poppoとかいうやつも多様な定義や多様な解釈が存在している中で不要なイチャモンばかりだな
さらに冒頭のこれも
> JavaScriptで演算子オーバーロードを実現しようとするのは筋が悪
301:い たまたま例としてJavaScriptを用いているだけなのにそれすら理解できていない okabeは使用言語と無関係に成り立つ話をしてるだろ > reduceは二項演算ではなく三項演算として捉えるべき これも些細なことであって例えばRustなら fold()は『入力列・初期値・演算関数』の三項演算だけど reduce()は『入力列・(初期値は入力列の先頭なので無指定)・演算関数』の二項演算 とはいえokabeの方もイテレータすら扱っていないからイマイチ
302:デフォルトの名無しさん
21/12/14 00:07:08.47 LYbWtya0.net
Rust関係ねーだろ
二度とその名前を口に出すな
303:デフォルトの名無しさん
21/12/14 10:41:11.49 QBQJlKEt.net
P2P方式の2D対戦ゲームを作りたいと考えています。
おすすめのゲームエンジンやライブラリはございますか?
304:デフォルトの名無しさん
21/12/14 11:46:02.04 mpAOsF0a.net
>>297
あくまでゲームを作ることが目的なんだったら普通にUnityとかでいいんじゃない?
どうしてもRust使いたいならサーバー側で使えばいい
305:デフォルトの名無しさん
21/12/14 12:45:44.37 QBQJlKEt.net
>>298
個人的にRustが好きなので技術向上のためにもRustで作りたいと考えています。
現在はAmethystとlibp2pを用いて開発しようと考えているのですが、如何せん知識が浅くこれで目的のものが作れるのか分かりません。
是非先人の知恵をお貸しください。
Rustでの開発にロミオとジュリエットの恋ほどの壁があるという場合は、大人しくC++かUnityで作成します・・・
306:デフォルトの名無しさん
21/12/14 13:10:49.68 +JRF3Q+g.net
そんだけの情報ではなんともいえん。
見通しが立たないものを試行錯誤で作る場合には
モジュールではなくレイヤで分割したほうがいいという考え方がある。
要するに機能不足でもバグだらけでもコードが整理されてなくてもいいからとにかく「動くもの」を作って
その上に足りないものをどんどん足していくという方法論だ。
よくわかってないなら小さいもので色々やってみて知識を積み重ねるべきで、
よくわからんまま目的に向かって邁進してもあんまり技術向上にはならんよ。
307:デフォルトの名無しさん
21/12/14 13:22:32.10 QBQJlKEt.net
>>300
ありがとうございます
色々試してみます
308:デフォルトの名無しさん
21/12/14 13:53:41.90 ZTFSAiNI.net
>>299
Amethystは開発中止になったから今からやるのは微妙かも
gamedev.rsに今アクティブなエンジンやゲームがスクショ付きで載ってるから
そこからイメージにあうものを探すといいかもしれない
309:デフォルトの名無しさん
21/12/14 15:30:19.02 QBQJlKEt.net
>>302
そうだったんですね・・・
時間は掛かるかもしれませんが、色んなものを試して自分に合う物を探すことにします
310:デフォルトの名無しさん
21/12/15 07:48:40.06 inpCEPk8.net
技術を高める目的なら windows-rs や wgpu-rs みたいな一段下から積み上げるのも楽しいよ。
ゲーム作る道のりは遠くなるけど、画面に三角形出したり、キーの入力受け付けたりするだけで達成感が出てくる。
311:デフォルトの名無しさん
21/12/17 12:43:03.76 jilrKB7M.net
URLリンク(forest-watch-impress-co-jp.cdn.ampproject.org)
エディタ自体はvscodeに勝つの難しそうだがGPUIとやらが定番GUIフレームワークにならないか期待
312:デフォルトの名無しさん
21/12/17 16:39:33.05 6JlgT7vi.net
既出だったらすいません
前から疑問だったのですが、出力におけるprint!マクロのような入力用マクロが標準ライブラリに用意されていない理由ってなんですか?
313:デフォルトの名無しさん
21/12/17 18:11:01.90 tWB5K5S1.net
>>297
1対多で、broadcast するチャットルームのバックエンドなら、
Ruby on Rails 6 のAction Cable(WebSocket)が基本
JavaScript は、React, Phaser とか
【Rails】(送信時のリロード無し!)Action CableでSlack風チャットアプリを作成、2019/12
URLリンク(www.youtube.com)
314:デフォルトの名無しさん
21/12/17 18:36:09.20 ePonqmC1.net
>>306
この辺読んで
URLリンク(github.com)
URLリンク(github.com)
315:デフォルトの名無しさん
21/12/17 23:52:56.58 6JlgT7vi.net
>>308
熟読させていただきました。
私としてはたった数十行のコードであること且つ他のほとんどの言語に存在しているものなのであっても良いのかなと思ったのですが、Rustの基本理念を考えると慎重になる理由も理解出来ました。
Rustの経験が浅い私目線ではあまり腑に落ちませんでしたが、皆さんはどう考えていますか?
316:デフォルトの名無しさん
21/12/18 15:05:52.37 JzdvFl4u.net
熟読はしてないけど、外部ライブラリで簡単に実現可能であるなら
直感的には、本体メンテナの苦労を増やすほど価値があると思えないし別にいらんかな
こういうIOとかCLIプロンプトらへんの機能ってどうしても好みが分かれるというか、
ユースケースによって必要な機能がかなり違ってきちゃうと思うし
317:デフォルトの名無しさん
21/12/18 20:46:47.80 w6oxugk6.net
C++のiostreamが失敗作だから慎重になるのもわかる笑
318:デフォルトの名無しさん
21/12/18 23:17:58.09 SfwydFh9.net
>>306
C言語でのprintf相当はあるのにscanf相当がstdにないのはなぜか?ですね
逆になぜprintf相当がRustの標準ライブラリにあるのか?を考えてみると
(1) 読み書きの非対称性
人間が読むためにプログラムが書くことはエラー表示を含めて利用必須かつ頻出
一方で人間が書いたものをプログラムが読むことはレア
ファイルや通信相手への読み書きは各プロトコル/各API/シリアライズ等で対象外
(2) コンパイラサポート
print!やformat!等は頻出するので効率面からコンパイラサポートが効率的
実際にそれらが利用しているformat_args!はコンパイラ内蔵マクロ
(3) 標準ライブラリ採用
外部ライブラリで実現可能なものは採用しないが基本
今回はコンパイラ内蔵マクロだから採用
つまり出力は頻度と効率化で採用がクリアされたけど
入力は外部ライブラリで十分かなと
319:デフォルトの名無しさん
21/12/19 02:25:28.05 KXG/wmTu.net
しかし競プロでみんなproconioとかいう謎の専用ライブラリ使ってるの見た目悪すぎて笑える
320:デフォルトの名無しさん
21/12/19 04:21:35.56 1R2jF/rb.net
競プロ全然知らないけどstdinに入力数値がくるからか
| 入力は以下の形式で標準入力から数値が与えられる。
| a b c d e
| 積が奇数なら Odd と、 偶数なら Even と出力せよ。
標準ライブラリだけ使うと毎回こんなの書くのは面倒だもんな
| use std::io::{stdin, BufRead, BufReader};
| println!("{}", if BufReader::new(stdin()).lines().next().unwrap().unwrap().split(' ').any(|s| s.parse::<isize>().unwrap() & 1 == 0) { "Even" } else { "Odd" });
>>313
そのproconioを使うとこうなるようだ
| proconio::input! { v: [isize; 5] }
| println!("{}", if v.into_iter().any(|n| n & 1 == 0) { "Even" } else { "Odd" });
321:デフォルトの名無しさん
21/12/19 06:54:36.03 KXG/wmTu.net
>>314
ワンライナー大好きかよ
322:デフォルトの名無しさん
21/12/19 07:00:31.55 1R2jF/rb.net
>>315
マルチラインだと標準ライブラリだけでもわかりやすくなる?
323:デフォルトの名無しさん
21/12/19 08:28:47.39 KXG/wmTu.net
>>316
あいやごめん。proconioのところはどうあがいてもプロコン専用のproconioが一番見やすいよ。それ用に特化されたライブラリだし
俺が大好きかよって言ったのは
println!("{}", if v.into_iter().any(|n| n & 1 == 0) { "Even" } else { "Odd" });
の部分
324:デフォルトの名無しさん
21/12/19 09:08:15.26 1R2jF/rb.net
どの言語でも三項演算子(相当)はワンライナーで書くんじゃね?
325:デフォルトの名無しさん
21/12/19 09:36:51.21 Tv9xxy1h.net
見た目汚いなw
326:デフォルトの名無しさん
21/12/19 09:42:08.31 1R2jF/rb.net
これでいいかね?
println!("{}", if v.into_iter().any(|n| n & 1 == 0) {
"Even"
} else {
"Odd"
});
327:デフォルトの名無しさん
21/12/19 09:57:56.98 KXG/wmTu.net
俺ならこんな感じかなあ
俺は頭悪いから、途中結果に一つ一つ名前つけないとわかんなくなっちゃうわ
まあワンライナー見た時に「グエー」って思っただけだからごめん。「大好きかよ」とかいっておいてなんだけどあんま気にしないで
use proconio::input;
fn main() {
input!(v: [usize; 5]);
let is_even = v.iter().any(|x| x % 2 == 0);
let result = if is_even {
"Even"
} else {
"Odd"
};
println!("{}", result);
}
328:デフォルトの名無しさん
21/12/19 10:13:36.41 1R2jF/rb.net
>>321
なるほど
じゃあ最初の標準ライブラリのみはどのように分けるのかな
println!("{}", if BufReader::new(stdin()).lines().next().unwrap().unwrap().split(' ').any(|s| s.parse::<isize>().unwrap() & 1 == 0) { "Even" } else { "Odd" });
329:デフォルトの名無しさん
21/12/19 10:17:01.07 KXG/wmTu.net
>>322
proconio使うw
Rustで競プロするにはIOごときに専用ライブラリ使うことになってなんかなあって思うけど、そう思いながら使う
330:デフォルトの名無しさん
21/12/19 12:54:53.29 XhCh7cuL.net
>>315
ワンライナーやクロージャでまとめて書くと所有権やライフタイムの問題が出にくいんだよ
他の言語と違ってRustで適切に分割して読みやすく書くのは初心者には難しい
331:デフォルトの名無しさん
21/12/19 13:44:27.43 JD8Mu2Jr.net
わけわからんくてビビってたらよく見るとPythonスレじゃなかった
332:デフォルトの名無しさん
21/12/19 15:04:40.83 9UFbsF2U.net
rustfmt使うでしょ普通
333:デフォルトの名無しさん
21/12/19 21:36:12.89 1R2jF/rb.net
>>326
println全体が改行されてしまった
println!(
"{}",
if BufReader::new(stdin())
.lines()
.next()
.unwrap()
.unwrap()
.split(' ')
.any(|s| s.parse::<isize>().unwrap() & 1 == 0)
{
"Even"
} else {
"Odd"
}
);
一方で分割するとこうなった
let cond = BufReader::new(stdin())
.lines()
.next()
.unwrap()
.unwrap()
.split(' ')
.any(|s| s.parse::<isize>().unwrap() & 1 == 0);
println!("{}", if cond { "Even" } else { "Odd" });
if式自体はワンライナーーで正解とrustfmtはおっしゃってる
334:デフォルトの名無しさん
21/12/19 21:50:26.03 gPlFWszB.net
ワンライナー云々はフォーマットの話ではないやろ
335:デフォルトの名無しさん
21/12/19 22:05:50.34 1R2jF/rb.net
例えば>>321がif式を5行に分割しているが
それもrustfmtにより1行に修正された
Rust標準ライブラリのソースでもワンライナー
bool.rs: if self { Some(t) } else { None }
result.rs: let n = if self.inner.is_some() { 1 } else { 0 };
time.rs: let prefix = if f.sign_plus() { "+" } else { "" };
>>328
では何の話かね?
336:デフォルトの名無しさん
21/12/19 22:37:44.04 D79ys1jH.net
構造と書式を区別できんのはヤバイで
337:デフォルトの名無しさん
21/12/19 23:10:30.41 ZhiL7Huf.net
心底どうでもいい。
保守性に貢献するどころかマイナスになるようなクソプライドコードの導入すんなよ。
338:デフォルトの名無しさん
21/12/19 23:14:48.39 4jRnwXL4.net
また性懲りもなくコードべたべた書いてんのか
339:デフォルトの名無しさん
21/12/19 23:21:45.51 cDs+Q4pL.net
rustfmtだから書式の話やで
340:デフォルトの名無しさん
21/12/19 23:25:17.24 /BRS7QS/.net
区別できなかったからrustfmtを持ち出したんやろ
どこに改行入れるべきかって話だと思ったんだろなww
341:デフォルトの名無しさん
21/12/19 23:41:46.15 1R2jF/rb.net
rustfmtを持ち出したのは俺じゃないぜ
あと今回のケースならここは分割するよな
let reader = BufReader::new(stdin());
let line = reader.lines().next().unwrap().unwrap();
let is_even = line.split(' ').any(|s| s.parse::<isize>().unwrap() & 1 == 0);
342:デフォルトの名無しさん
21/12/19 23:48:52.91 m2ZvKw+8.net
汚コード
343:デフォルトの名無しさん
21/12/19 23:59:56.43 cDs+Q4pL.net
問題文が>>314でこうなってるから
>> | 入力は以下の形式で標準入力から数値が与えられる。
>> | a b c d e
if let [a, b, c, d, e] = line
.splitn(5, ' ')
.map(|s| s.parse::<isize>().unwrap())
.collect::<ArrayVec<_, 5>>()[..] {
あとこれでいい
let is_even = a & b & c & d & e & 1 == 0;
344:デフォルトの名無しさん
21/12/20 05:23:59.31 XPDM+Al+.net
そういえばいつの間にか
let variable = 3;
println!("{variable}");
みたいな書き方が出来るんだけどこれ前からだっけ?
345:デフォルトの名無しさん
21/12/20 05:50:08.07 MpI5dMic.net
>>338
それはまだnightlyだけですよ
来月の1.58からstableになって使えるようになる予定
346:デフォルトの名無しさん
21/12/20 11:31:40.46 vxPhPqzv.net
>>335
構造化の基本を学びましょう
347:デフォルトの名無しさん
21/12/20 18:38:24.90 +mZvzmRI.net
どちらのコードも正解
348:デフォルトの名無しさん
21/12/20 18:57:17.41 rubGoZ9q.net
コードに「間違い」はあっても「正解」はない
349:デフォルトの名無しさん
21/12/20 19:04:29.92 MpI5dMic.net
間違っているコードは出ていないような
問題視してる人は具体的に何を問題にしているの?
350:デフォルトの名無しさん
21/12/20 20:55:59.37 lwZpjeWf.net
アスぺ思考はこれだから
351:デフォルトの名無しさん
21/12/21 11:25:19.41 TTfv6HaA.net
URLリンク(doc.rust-jp.rs)
ここの最後のところに
パターンと関わるマッチガードの優先度は、以下のように振る舞います:
(4 | 5 | 6) if y => ...
以下のようにではありません:
4 | 5 | (6 if y) => ...
とあるんですが、後者のようになってほしい場合は4|5と6 if yを別々に書くしか無いですか?
352:デフォルトの名無しさん
21/12/21 11:56:47.99 BV9oeByN.net
>>345
ガード構文がこう「『Pattern』 if 『Expression』」なのでそうなりますね
353:デフォルトの名無しさん
21/12/22 20:44:35.79 7pEHjDF/.net
sixtyfpsってどう?
354:デフォルトの名無しさん
21/12/22 22:58:00.27 mfli1g17.net
> しかし競プロでみんなproconioとかいう謎の専用ライブラリ使ってるの見た目悪すぎて笑える
いや、ほぼ公認のクレートなんだが・・・
謎の専用ライブラリとかいってる時点で、お察しか?
あと、見た目悪いってどういうことなんかね
includeしたら見た目悪すぎなわけ?
355:デフォルトの名無しさん
21/12/22 23:53:48.10 Wk3NOZd2.net
Rust bookの以下の記述について質問です
URLリンク(doc.rust-jp.rs)クロージャを返却する
> 以下のコードは、クロージャを直接返そうとしていますが、コンパイルできません:
>
> fn returns_closure() -> Fn(i32) -> i32 {
> |x| x + 1
> }
> コンパイラには、クロージャを格納するのに必要なスペースがどれくらいかわからないのです。
> この問題の解決策は先ほど見かけました。
>
> fn returns_closure() -> Box<Fn(i32) -> i32> {
> Box::new(|x| x + 1)
> }
とBox化しなさいと書かれているのですが
以下のようにimplを付けるとBoxを使わなくてもコンパイルが通り動きました
fn make_closure_add1() -> impl Fn(i32) -> i32 {
|x| x + 1
}
このimpl付加は暗に自動的にBox化されているということなのでしょうか?
356:デフォルトの名無しさん
21/12/23 00:23:38.98 p+r9sE2/.net
ここを読む
URLリンク(doc.rust-lang.org)
357:デフォルトの名無しさん
21/12/23 07:21:11.33 esNMmzKz.net
>>348
そりゃあcratesio に上がったものは空っぽでもゴミでも全部公認だわなw
358:デフォルトの名無しさん
21/12/23 15:17:54.33 BEeWZFks.net
>>342
逆だ、コードは常に「正解」で動いていて「間違い」ない。
あんたの書いたバグもコンピューターにとっては一部の隙もなく正解であり、書かれたその通りに動き、無慈悲である。
同じ目的で、多数、あるいは二人の人が書いたコードで違いが出るのは「表現の違い」であり「間違い」と決めつける
のは人間の主観や嗜好でしかなく、仮にコードへ正誤を求めるなら明確に表現できていなればならず、矛盾が生じる
359:デフォルトの名無しさん
21/12/23 15:32:21.87 GoKXBRn5.net
コンパイルが通らないコードも正解なのかい?
360:デフォルトの名無しさん
21/12/23 15:39:30.16 9VjYa60R.net
えらいポエミーやな
361:デフォルトの名無しさん
21/12/23 18:55:13.24 TD851Muu.net
”動いていて”と言っているからコンパイルは通ってる前提だろう、”コードへ正誤を求める”といっているから
仮にコンパイルが通らないコードは明確にそれ(誤り・間違い)が表現できている
ポエミーなのはその通りだろう
362:デフォルトの名無しさん
21/12/23 21:22:53.78 NwYcCv97.net
>>349
Boxとはヒープを使うということです
Rustではコードで明示的に指定しない限り勝手にヒープが使われることはないです
(もちろんBox以外にもVecやStringなどヒープを使うものを使ってもそれは明示的に指定したことになります)
その Box<Fn(i32) -> i32> は今は Box<dyn Fn(i32) -> i32> と書く必要があります
では本題の impl Fn(i32) -> i32 と書いた場合はどうなるのでしょうか?
以下のように3種類のクロージャを作ってサイズや型を表示させてみると
fn main() {
let direct_closure = |x: i32| x + 1;
let impl_closure = make_impl_closure();
let box_closure = make_box_closure();
println!("{} {}", std::mem::size_of_val(&direct_closure), type_of(&direct_closure));
println!("{} {}", std::mem::size_of_val(&impl_closure), type_of(&impl_closure));
println!("{} {}", std::mem::size_of_val(&box_closure), type_of(&box_closure));
}
fn make_impl_closure() -> impl Fn(i32) -> i32 {
|x| x + 1
}
fn make_box_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
fn type_of<T>(_: &T) -> &'static str {
std::any::type_name::<T>()
}
実行結果は以下のように表示されます
0 tmp::main::{{closure}}
0 tmp::make_impl_closure::{{closure}}
16 alloc::boxed::Box<dyn core::ops::function::Fn<(i32,)>+Output = i32>
つまりimplでは直接クロージャ指定したのと全く同じです
(上記では定義した関数名だけが異なる)