Rust で実行中にバッファーの次元解釈を変更できる DimensionShiftableBuffer と翻訳時に任意の次元解釈をVec<T>に追加する vec-dimension-shift を公開しました。のメモ
- dimension_shift_buffer
- vec-dimension-shift
前提として、どちらも単一のヒープに全体が連続したメモリーアドレスを持つバッファーを扱う、という事があります。そのうえで、バッファーを任意次元に再解釈します。
dimension_shiftable_buffer
実行時に任意の次元にバッファーの解釈を変更したビューを用いてバッファーを扱える、そういうものです。こちらの実装はすべて Safe です。 unsafe
せずに Vec<T>
を実行時に任意次元へ再解釈する方法は思いつかなかったので getter(get,pop,remove)/setter(push,append) と for_each
を用意しました。こちらの利点は「実行時に」「任意次元へ」です。
// make a 2d-empty DimensionShiftableBuffer let mut dsb = DimensionShiftableBuffer::<u8>::new(vec![], 2).unwrap(); // push a 2d-datum dsb.push(&[0u8, 1]).unwrap(); // push a 2d-datum dsb.push(&[2u8, 3]).unwrap(); // append a 2d-datum sequence dsb.append(&[4u8, 5, 6, 7, 8, 9, 10, 11]).unwrap(); for index in 0..dsb.len().unwrap() { // get a 2d slice assert_eq!(dsb.get(index).unwrap(), &[index as u8 * 2, index as u8 * 2 + 1]); } // shift dimension to 3 from 2 dsb.shift_dimension(3).unwrap(); // push a 3d-datum dsb.push(&[12u8, 13, 14]).unwrap(); // get a 3d-datum assert_eq!(dsb.get(0).unwrap(), &[0u8, 1, 2]); assert_eq!(dsb.get(1).unwrap(), &[3u8, 4, 5]); assert_eq!(dsb.get(2).unwrap(), &[6u8, 7, 8]); assert_eq!(dsb.get(3).unwrap(), &[9u8, 10, 11]); assert_eq!(dsb.get(4).unwrap(), &[12u8, 13, 14]); // get a linear slice let linear_slice = dsb.as_slice(); assert_eq!(linear_slice, &[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
vec-dimension-shift
こちらは翻訳時にN次元から実際に対応する具体的な次元の次元再解釈機能を選択的に Vec<T>
へ付与する trait
集です。 usize
次元のすべてのパターンを lib に埋め尽くすわけにはいかないので、2..16次元は features で選択的に使用可能に、 default で 2,3,4 次元の trait
s を定義としつつ、 make_vec_dimension_shift_n_dimension!
マクロをユーザーが任意に呼べるように pub り、 crate のユーザーが任意に欲しい次元を選択的に扱えるようにしています。
こっちの中身は黒魔術と unsafe
でできています。次元再解釈時の境界チェック、直接の変換が不可能な場合のErr
|truncate|paddingなど基本的には安全に使いやすいように実装していますが、N次元から1次元化するための flatten の実装では Vec
の実装とメモリーレイアウトをにゃーんしたりしていたりします。
use vec_dimension_shift::{ VecDimensionShift2D, VecDimensionShift2DFlatten, VecDimensionShift3D, VecDimensionShift3DFlatten }; fn d2_and_d3() { let original = vec![0.0, 1.1, 2.2, 3.3, 4.4, 5.5]; dbg!(&original); let mut d2_shifted = original.as_2d_array().unwrap(); dbg!(&d2_shifted); assert_eq!(d2_shifted[0], [0.0, 1.1]); assert_eq!(d2_shifted[1], [2.2, 3.3]); assert_eq!(d2_shifted[2], [4.4, 5.5]); d2_shifted[1][1] = -1.0; let flatten = d2_shifted.as_flatten(); dbg!(&flatten); let mut d3_shifted = flatten.as_3d_array().unwrap(); dbg!(&d3_shifted); assert_eq!(d3_shifted[0], [0.0, 1.1, 2.2]); assert_eq!(d3_shifted[1], [-1.0, 4.4, 5.5]); d3_shifted[1][1] = -2.0; let flatten = d3_shifted.as_flatten(); dbg!(&flatten); assert_eq!(flatten, vec![0.0, 1.1, 2.2, -1.0, -2.0, 5.5]) }
ちなみに、 1D -> 2D とした後に 1D へ flattening せず、 1D -> 2D -> 3D と次元再解釈すると、
use vec_dimension_shift::make_vec_dimension_shift_n_dimension; fn n_dimension_macro_generator() { make_vec_dimension_shift_n_dimension! { VecDimensionShift2D, VecDimensionShift2DFlatten, as_2d_array_no_check, to_2d_array_no_check, as_2d_array, to_2d_array, as_2d_array_truncate, to_2d_array_truncate, as_2d_array_padding, to_2d_array_padding, 2 } make_vec_dimension_shift_n_dimension! { VecDimensionShift3D, VecDimensionShift3DFlatten, as_3d_array_no_check, to_3d_array_no_check, as_3d_array, to_3d_array, as_3d_array_truncate, to_3d_array_truncate, as_3d_array_padding, to_3d_array_padding, 3 } let original = vec![0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10, 11.11]; dbg!(&original); let d2 = original.as_2d_array().unwrap(); assert_eq!(d2[0], [0.0, 1.1]); assert_eq!(d2[1], [2.2, 3.3]); assert_eq!(d2[2], [4.4, 5.5]); assert_eq!(d2[3], [6.6, 7.7]); assert_eq!(d2[4], [8.8, 9.9]); assert_eq!(d2[5], [10.10, 11.11]); dbg!(&d2); let d3 = d2.as_3d_array().unwrap(); assert_eq!(d3[0], [[0.0, 1.1], [2.2, 3.3], [4.4, 5.5]]); assert_eq!(d3[1], [[6.6, 7.7], [8.8, 9.9], [10.10, 11.11]]); dbg!(&d3); }
こういう多N次元(N次元×N次元×…)も作れます。