问题
I have two (very large) arrays foo
and bar
of the same type. To be able to write some nice code, I would like to obtain a read-only slice, result
, of the concatenation of the two arrays. This operation must run in O(1) time and space.
Array access for result
must also be in O(1). More generally, if result
were the concatenation of k
array slices, an arbitrary array access for result
should run in O(k
).
I do not want to copy any elements of foo
nor bar
.
This would seem to be easy to implement into the Rust core, but no amount of searching has brought me a solution.
回答1:
There isn't a predefined type, but you can easily create your own by implementing the Index
trait for a type that holds both your slices:
use std::ops::Index;
struct Slice<'a, T: 'a>(&'a[T], &'a[T]);
impl<'a, T: 'a> Index<usize> for Slice<'a, T> {
type Output = T;
fn index(&self, index: usize) -> &T {
if index < self.0.len() {
&self.0[index]
} else {
&self.1[index - self.0.len()]
}
}
}
More generally, if
result
were the concatenation ofk
array slices, an arbitrary array access forresult
should run in O(k
).
You can get slice access in O(log(k))
, if your slice concatenation is O(k)
, by creating an array that holds the cumulative lengths of the slices and using a binary search to find the actual slice to index into.
This would require a macro, because we don't have a good enough constant evaluator yet and no value generics.
回答2:
For n arrays, you can implement it using a Vec
like below:
use std::ops::Index;
struct VecSlice<'a, T: 'a>(Vec<&'a [T]>);
impl<'a, T> Index<usize> for VecSlice<'a, T> {
type Output = T;
fn index(&self, mut index: usize) -> &T {
for slice in self.0.iter() {
if index < slice.len() {
return &slice[index];
} else {
index -= slice.len();
}
}
panic!("out of bound");
}
}
And then access it like an array, just don't go out of bound.
fn main() {
let a1 = [0, 1, 2];
let a2 = [7, 8, 9];
let a = VecSlice(vec!(&a1, &a2));
println!("{}", a[4]);
}
This prints out
8
回答3:
I'm afraid what you are asking is pretty much impossible if you require the result to be an actual slice. A slice is a view into a block of memory. Contiguous memory. If you want a new slice by combining two other slices you have to copy the contents to a new location, so that you get a new contiguous block of memory.
If you are satisfied just concatenating by copying SliceConcatExt
provides the methods concat
and join
on slices, which can be used on slices of custom types as long as they implement Clone
:
#[derive(Clone, PartialEq, Debug)]
struct A {
a: u64,
}
fn main() {
assert_eq!(["hello", "world"].concat(), "helloworld");
assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]);
assert_eq!([[A { a: 1 }, A { a: 2 }], [A { a: 3 }, A { a: 4 }]].concat(),
[A { a: 1 }, A { a: 2 }, A { a: 3 }, A { a: 4 }]);
}
Note that even though SliceConcatExt
is unstable, the methods themselves are stable. So there is no reason not to use them if copying is OK. If you can't copy you can't get a slice. In that case, you need to create a wrapper type, as explained in the answer of ker.
来源:https://stackoverflow.com/questions/37585808/concatenate-array-slices