LINUX.ORG.RU

Ищу аналог union/enum/adt для Python/Go

 , ,


1

2

Интересуют не либы, а идиоматическое решение задачи вида:

Rust:

enum Data {
    Number(u32),
    String(String),
}

let mut items = Vec::new();
items.push(Data::Number(5));
items.push(Data::String("text".to_string()));

for item in items {
    match item {
        Data::Number(n) => println!("Number {}", n),
        Data::String(ref s) => println!("String {}", s),
    }
}

C++:

std::vector<std::variant<int, std::string>> items;
items.push_back(5);
items.push_back("text");

for (const auto &item : items) {
    if (const auto n = std::get_if<int>(&item)) {
        std::cout << "Number " << *n << std::endl;
    } else if (const auto &s = std::get_if<std::string>(&item)) {
        std::cout << "String " << *s << std::endl;
    }
}

Как это повторить на python и go?

То есть нужно заполнить вектор/список/массив объектами разных типов (желательно с гарантиями) и затем пробежаться по ним, забирая значение каждого типа.

★★★★★

Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Он может откуда-то приходить, матчиться, и потом «куда-то» передаются его компоненты.

Значит общая ф-ция будет dispatch, типа


class C1:
    def __init__(self, a):
         self.a = a

    def dispatch(self):
          send_a_somewhere(self.a)


class C2:
    def __init__(self, b):
         self.a = b

    def dispatch(self):
          send_b_somewhere(self.b)

def main():
    s = receive_from_somewhere()
    s.dispatch()

provaton ★★★★★
()
Ответ на: комментарий от pawnhearts

Но в стандартном Python и статических проверко нет, и сигнатуры - просто докстринги. По крайней мере, пока mypy не интегрировали в CPython.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Если сделать свой sum type, обмазаться макросами и использовать факт, что компилятор предупреждает об отсутствующем варианте в switch по enum - да. Может, и для variant такое можно провернуть.

Ретрограды, блин. В C++17 ваши страдания уже, частично, облегчили: https://en.cppreference.com/w/cpp/utility/variant/visit (см. в примере фокус с overloaded):

// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
...
    for (auto& v: vec) {
        // 4. another type-matching visitor: a class with 3 overloaded operator()'s
        std::visit(overloaded {
            [](auto arg) { std::cout << arg << ' '; },
            [](double arg) { std::cout << std::fixed << arg << ' '; },
            [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
        }, v);
    }
Из практики: такой overloaded для себя пишется один раз, затем переиспользуется.

PS. Почему он не включен в stdlib не знаю.

eao197 ★★★★★
()
Ответ на: комментарий от pawnhearts

Сторонний статический анализатор - это хорошо, конечно. Но лучше когда сам компилятор делает проверки во время формирования нативного кода.

provaton ★★★★★
()
Ответ на: комментарий от provaton

Интересная точка зрения. А если варианты не передаются, а, скажем, печатаются - должен быть общий метод метод print? Т.е. на каждое возможное использование - по общему методу?

tailgunner ★★★★★
()
Ответ на: комментарий от eao197

Ретрограды, блин. В C++17

Не у всех на целевой системе есть Си++17.

ваши страдания уже, частично, облегчили: https://en.cppreference.com/w/cpp/utility/variant/visit

Да вот ХЗ, облегчение ли это. Кроме того - в коде 3 ветки, но в variant четыре типа. Предупреждение будет?

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

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

provaton ★★★★★
()
Ответ на: комментарий от provaton

Да, кстати, если у мемберов энума параметры одного типа (или даже «похожих» типов), то питоновский вариант на тайпах не работает совсем.

provaton ★★★★★
()
Ответ на: комментарий от provaton

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

Подозреваю, что этот набор будет состоять из get_if. Поверх него точно можно построить всё остальное.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Трудно сказать в общем случае.

Кстати, в питоне с недавних пор есть энумы в стандартной либе. Только варианты без параметров. Как так (

provaton ★★★★★
()
Ответ на: комментарий от tailgunner

Не у всех на целевой системе есть Си++17.

Ты сейчас про себя или про RazrFalcon-а?

Если про себя, то у тебя и Rust-а нет, но трындеть же тебе это не мешает.

Да вот ХЗ, облегчение ли это.

Да.

Кроме того - в коде 3 ветки, но в variant четыре типа. Предупреждение будет?

В коде в одной ветке auto в качестве типа параметра. Поэтому работает как default. Не делай auto, так получишь не предупреждение, а ошибку компиляции.

eao197 ★★★★★
()
Ответ на: комментарий от tailgunner

Я думаю никогда не интегрируют, потому что это будет медленно и большинство кода сигнатурами не покрыто все равно.

pawnhearts ★★★★★
()
Ответ на: комментарий от pawnhearts

Статическую проверку можно делать, например, при первом запуске на этапе компиляции в байткод.

provaton ★★★★★
()
Ответ на: комментарий от pawnhearts

Я думаю никогда не интегрируют, потому что это будет медленно

Думаю, никто не требует проверки типов при каждом запуске. Интегрировать просто для того, чтобы он всегда был под рукой.

и большинство кода сигнатурами не покрыто все равно.

Отнюдь не всё равно. Возможно, когда просто объединяешь библиотеки, это и не важно, но когда пишешь свой код - очень важно.

tailgunner ★★★★★
()
Ответ на: комментарий от eao197

Не у всех на целевой системе есть Си++17.

Ты сейчас про себя или про RazrFalcon-а?

Про себя. Ты же на мою реплику ответил.

Если про себя, то у тебя и Rust-а нет, но трындеть же тебе это не мешает.

У меня есть Rust (как и Си++17). А вот на целевой системе нет ни того, ни другого.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

У меня есть Rust (как и Си++17). А вот на целевой системе нет ни того, ни другого.

Прости, для меня это слишком сложно.

Если ты про то, что для себя дома ты можешь писать на чем угодно, а на работе только на подмножестве C++0x, то прости, проблемы специфических производств меня не волнуют, пока мне не платят за их решение.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Если ты про то, что для себя дома ты можешь писать на чем угодно, а на работе только на подмножестве C++0x, то прости, проблемы специфических производств меня не волнуют

Это очень важно. Спасибо, что поделился.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от eao197

Не делай auto, так получишь не предупреждение, а ошибку компиляции.

Не получу. При этом я могу указать там вообще любые типы, даже те, которых нет в variant. При этом ещё и implicit cast разращён. Что делает этот код бесполезным.

Ну и выглядит страшно, что ппц.

RazrFalcon ★★★★★
() автор топика
Последнее исправление: RazrFalcon (всего исправлений: 2)
Ответ на: комментарий от MyTrooName

Не. Я его вместо баша использую. То есть на уровне макаки.

RazrFalcon ★★★★★
() автор топика

Как это повторить на python и go?

Если ты ожидаешь прямую кальку с Rust без использования библиотек, то ты её не увидишь в этом треде, потому что в Go нет ADT.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)

извращение

угу. ирл я бы так сделал, если очень надо обозначить структуру данных

class Data:
    pass

class Number(Data):
    pass

class String(Data):
    pass

но по умолчанию просто списка из строк и чисел достаточно

MyTrooName ★★★★★
()
Ответ на: комментарий от RazrFalcon

если не нужен базовый класс, тебя должен устроить просто список интов и строк. Simple is better than complex

MyTrooName ★★★★★
()
Последнее исправление: MyTrooName (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Я хочу сказать, что компилируется вот это: https://godbolt.org/z/2_BR1e

Да ты бы взял на себя труд и оформил бы минимальный пример, чтобы в портянках не ковыряться и не думать, что ты хотел сказать.

Если в визиторе не будет хотя бы одного типа, который есть в варианте, то он не скомпилируется. Либо покажи обратное.

Если в визиторе есть какой-то другой тип, то здесь все просто:

- если этот тип вообще никак не относится к типам в варианте, он должен быть просто проигнорирован;

- если этот тип может быть неявно приведен из одно из типов из варианта, то такой визитор скомпилируется (как в примере с типом foo из cppreference). Собственно, как здесь ничего нового для C++ нет.

Что именно тебя возмущает?

eao197 ★★★★★
()
Ответ на: комментарий от RazrFalcon

Ещё в плюсах можно сделать по старинке - enum-ами и union-ами в единой структуре.
Т.е. ручками реализовать растовские энумы.
Только хранить в union-е указатель на string придётся.
Вопщем так себе конечно решение.
¯\_(ツ)_/¯
Но зато switch-case будет работать.

WatchCat ★★★★★
()
Ответ на: комментарий от eao197

Да ты бы взял на себя труд и оформил бы минимальный пример

Это пример, который дал ты. И он вполне минимальный.

чтобы в портянках не ковыряться и не думать, что ты хотел сказать.

Спроси. Или просто не ковыряйся - никто не заставляет.

Что именно тебя возмущает?

Да ничего, в общем. TIL что визитор variant-а ни разу не замена match.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

И он вполне минимальный.

Ахринеть, дорогая редакция.

Вот минимальный был: Ищу аналог union/enum/adt для Python/Go (комментарий)

И вот был минимальный: https://wandbox.org/permlink/nhQXPz2cvXlHrHtX

А ты скопипастил всю портянку с cppreference. И разбирайся потом с тем, что тебе не нравится.

Спроси.

Спросил, но ты же не ответил, ты вообще шлангуешь в своей обычной манере.

что визитор variant-а ни разу не замена match.

Где-то утверждалось обратное? Речь шла о том, что вместо костылей с собственными sun-types и макросами, можно взять возможности из C++17. И это будет лучше и понятнее, чем ты сам накостылишь.

Нормальный же match может появиться лишь на уровне языка.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Спросил, но ты же не ответил, ты вообще шлангуешь в своей обычной манере.

Если ты не понял ответа «в визиторе можно указать тип, которого нет в варианте» - я тебе не доктор.

что визитор variant-а ни разу не замена match.

Где-то утверждалось обратное?

Да. Ты предлагал этот визитор как замену match. Так вот, в роли замены match этот визитор гораздо хуже, чем макрос + switch.

И да, никто не ждет от современного Си++ настоящего match - обсуждаются только замены. Эрзацы.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от WatchCat

Компилятор знает, что членами union могут быть только объекты без конструкторов (или с тривиальными конструкторами - вечно забываю эти детали). Так что построить variant на union не получится.

tailgunner ★★★★★
()
Ответ на: комментарий от WatchCat

потому я и говорил, что хранить придётся указатели.

Да нет же. Хранить придется, упрощенно говоря, массив байтов размером с наибольший хранимый объект + тег типа. Посмотри любую условно-переносимую реализацию варианта - там aligned_storage или его самодельный аналог. Указателя в реализации variant может не быть вообще.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Наверняка оно еще допускает и расширение набора допустимых типов, так что... это не эквивалент.

https://play.golang.org/p/efCdl7dOtAI - так не допускает.

Как минимум, такое решение молча допускает код, который обрабатывает не все варианты

Это да. Тут разве что костыль городить вроде линтера. Но в рантайме это работать не будет.

feofan ★★★★★
()
Последнее исправление: feofan (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Если ты не понял ответа «в визиторе можно указать тип, которого нет в варианте» - я тебе не доктор.

Я не понял, почему это такая проблема.

На практике при переборе типов из варианта требуется гарантия от компилятора, что для всех типов есть обработчики. Эта гарантия предоставляется.

Так что в реальном коде меняешь свой вариант и потом ловишь ошибки от компилятора в тех местах, где визитор на базе overloaded использовался.

Если ты в визитор добавил какой-то левый тип, которого в варианте не было... Ну да, формально это недостаток. Который вряд ли тебе помешает на практике.

Поэтому с точки зрения потрындеть на форуме да, ты нашел к чему доколупаться. Впрочем, как было сказано, ты ни Rust, ни C++17 в работе не используешь, только трындишь.

Да. Ты предлагал этот визитор как замену match.

Я сказал, что для тех, кто пытается сделать замену match на макросах, в C++17 завезли более простой и удобный механизм. Который позволяет экономить время и силы.

Альтернативой match-а он в принципе не является, т.к. не позволяет делать ни декомпозицию агрегатов, ни задавать ограничения (guard-ы).

eao197 ★★★★★
()
Ответ на: комментарий от eao197

На практике

в реальном коде

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

Deleted
()
Ответ на: комментарий от tailgunner

Какой «целевой системе»? Имплементация variant в хедере

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.