Есть ли способы рекурсивно сгладить кортежи?

В Rust есть ли способ использовать traitс и impls (рекурсивно) сгладить кортежи?

Если это помогает, то, что работает с N вложенными парами, является хорошим началом

trait FlattenTuple {
    fn into_flattened(self) -> /* ??? */
}

// such that
assert_eq!((1, (2, 3)).into_flattened(), (1, 2, 3))

Было бы еще лучше, если бы можно было расширять работу с любым типом вложенных кортежей, например:

assert_eq!(((1, 2), 2, (3, (4, 5))).into_flattened(), (1, 2, 2, 3, 4, 5))

2 ответа

Может быть, для некоторых небольших определений "сгладить", но на самом деле не совсем.

Начнем с наиболее конкретной реализации:

trait FlattenTuple {
    fn into_flattened(self) -> (u8, u8, u8);
}

impl FlattenTuple for (u8, (u8, u8)) {
    fn into_flattened(self) -> (u8, u8, u8) {
        (self.0, (self.1).0, (self.1).1)
    }
}

Тогда сделайте это немного более общим:

trait FlattenTuple {
    type Output;
    fn into_flattened(self) -> Self::Output;
}

impl<A, B, C> FlattenTuple for (A, (B, C)) {
    type Output = (A, B, C);

    fn into_flattened(self) -> Self::Output {
        (self.0, (self.1).0, (self.1).1)
    }
}

И затем повторите для каждой возможной перестановки:

impl<A, B, C, D, E, F> FlattenTuple for ((A, B), C, (D, (E, F))) {
    type Output = (A, B, C, D, E, F);

    fn into_flattened(self) -> Self::Output {
        ((self.0).0, (self.0).1, self.1, (self.2).0, ((self.2).1).0, ((self.2).1).1)
    }
}

Эти две реализации покрывают ваши два случая.

Тем не менее, вам нужно будет перечислить каждый тип ввода, который вы хотите, вероятно, с помощью генерации кода. Я не знаю, как "проверить" тип ввода и затем "склеить" его в тип вывода.

Вы даже можете попытаться написать что-то несколько рекурсивное:

impl<A, B, C, D, E, F> FlattenTuple for (A, B)
    where A: FlattenTuple<Output = (C, D)>,
          B: FlattenTuple<Output = (E, F)>,
{
    type Output = (C, D, E, F);

    fn into_flattened(self) -> Self::Output {
        let (a, b) = self;
        let (c, d) = a.into_flattened();
        let (e, f) = b.into_flattened();

        (c, d, e, f)
    }
}

Но это быстро столкнется с проблемами базового случая: терминал 42 не реализует FlattenTupleи если вы попытаетесь impl<T> FlattenTuple for T вы столкнетесь с противоречивыми реализациями черт.

Я реализовал это со специализацией и автоматической чертой.

docs.rs

GitHub РЕПО


Использование в основном

assert_eq!((1, (2, ((3,) (4, 5)), (), 6).flatten(), (1, 2, 3, 4, 5, 6));

Удаляет все пустые кортежи () (как питон).


Если вы пишете общий код, вам нужно добавить

where (A, B): Flatten

rustc хочет этого, потому что Flatten реализуется, только если длина выходного кортежа меньше 13.

Другие вопросы по тегам