Godot: Сопоставление с образцом (переключатель) для GDScript

Созданный на 23 сент. 2016  ·  66Комментарии  ·  Источник: godotengine/godot

Я хотел бы предложить добавить сопоставление с образцом в GDScript, и я хотел бы его реализовать.

На данный момент в GDScript нет оператора switch - вам нужно связать несколько операторов if.

_ (Весь синтаксис здесь всего лишь первый набросок, предложения приветствуются) _

Сопоставление с образцом

Сопоставление с образцом - это проверка структуры и содержимого значения для выполнения перехода.

Таким образом, для простых значений (например, чисел, строк) он действует как статус переключения.

var num = 42
match num:
    0:  print("Zero")
    23: print("Half way there")
    42: print("The answer")

Отличие от оператора switch том, что эти случаи могут быть вложенными. Например, вы можете сопоставить содержимое массива.

var array = [13, "37", ["test", "array"]]
match array:
    [13, "37", ["test", "array"]]: print("Wow, a perfect match!")
    [13, 37, ..]:                  print("l33t man") # .. matches the rest of the array
    []:                            print("empty array")

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

Разрушение ценностей

Вместо простого ветвления в зависимости от равенства значений можно было бы привязать новые переменные.

Когда встречается первый шаблон, соответствующий значению, будет выполнена соответствующая ветвь. Если шаблон не соответствует, пробуется следующий. Поэтому * более общие шаблоны следует определять под более конкретными.

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

var array = [13, "37", ["test", "array"]]
match array:
    [13, "37", [var a, var b]]: print(a, ", ", b) # a = "test", b = "array"
    var n:                      print("Unknown array structure: " + str(n))

При сопоставлении словарей только значение может быть привязано. например, {"test": var a}

Простое совпадение по новому идентификатору равносильно случаю default в switch -выражениях.

_Дай мне знать, что ты думаешь _: blush:

feature proposal gdscript

Самый полезный комментарий

Для людей, выступающих за switch против match . Я, честно говоря, не вижу для этого веской рациональной причины

Это будет немного долго, не потому, что я так сильно забочусь об этом (я, вероятно, почти никогда сам не буду использовать switch из match ), а потому, что мне нужен конво быть более конструктивным, а не теряться в путанице.

Вот аргументы против match , из того, что я могу собрать:

  • у матча нет провалов
  • переключатель более знакомый

Начнем с аргумента 1:


match делает все, что делает switch, за исключением провала по умолчанию.
Однако по умолчанию падение составляет:

  1. практически никогда не использовался (за 20 лет программирования я ни разу не видел программы, в которой переключатель был бы реализован без break после каждого case . Я уверен, что он существует, но никогда не видел его )
  2. Вызывает ошибки (легко забыть, и компилятор не выдает ошибок)
  3. Если когда-либо использовалось, исключение, а не общий вариант использования (в этом случае более разумным является решение проблемы с помощью continue ).

TL; DR : match делает все, что switch делает хорошо, избегая ловушек switch , _and_ допускает более глубокую семантику, если программист желает их использовать (не в случае с switch , который менее гибкий, чем обычный ifs )


Аргумент в пользу знакомства также ошибочен, поскольку GD - это собственный язык, и не должно возникнуть проблем с изучением одного нового слова.
В качестве альтернативы аргумент знакомства должен быть расширен до: пробелов в качестве синтаксиса, elif , без фигурных скобок, один файл = один класс, ... Все это может быть или не быть знакомым, в зависимости от того, на каком языке вы использовал раньше. Например, для меня ничего из вышеперечисленного не подходит, а вот match подходит.
В Python даже нет switch (и не зря), поэтому даже обращение к синтаксической близости GD с Python не является допустимым аргументом.

Но, если предположить, что переключатель «знаком» (что, опять же, действительно зависит от того, с какого языка вы пришли, но допустим, что это так, ради аргумента), для программиста, ищущего документ и заменяющего

switch val:
    case a: 
        do_something()
        break;
    case b: 
        do_something_else()
        break;

с участием:

match val:
    a: 
        do_something()
    b: 
        do_something_else()

... банально.
Практически нет никакой кривой обучения. Для того, кто читает код, так же легко понять, что происходит, даже если вы никогда раньше не видели match . Кстати, именно так я узнал о конструкции match : читал код и натыкался на него. Мне не потребовалось больше доли секунды, чтобы понять, что это всего лишь чистильщик switch без break s и case s. Конечно, для понимания того, насколько мощно это было необходимо для чтения документации, но для понимания этого достаточно, чтобы использовать его везде, где можно было бы использовать switch , требуется от 1 до 10 секунд обучения.

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

TL, DR : аргумент о знакомстве не работает; Во-первых, это субъективно. Во-вторых, даже если предположить, что это правда, для программиста изучение немного другого синтаксиса является нормой для курса, а для непрограммистов совпадение с меньшей вероятностью будет ошибочным, но его немного легче изучить (тот же синтаксис минус одна конструкция ).


Таким образом, это предложение по улучшению языка:

  • доказано, что он семантически эффективен и представляет собой ценную абстракцию (что продемонстрировано всеми языками, которые его реализовали)
  • легче учиться и более защищено от ошибок для новичков, без каких-либо скрытых вещей, на которые нужно обратить внимание
  • позволяет использовать более мощные абстракции, чем его альтернатива
  • ... при этом не только не добавляя раздувания, но и удаляя некоторые
  • не создает дополнительной когнитивной нагрузки на программиста (не более чем запоминание elif вместо else if )

Все 66 Комментарий

Получите: +1: от меня :) сделайте haskell: P

Мне это нравится, потому что синтаксис в основном эквивалентен переключателю (пропадание полосы, но кому это все равно нужно), но при необходимости может быть более мощным

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

var num = 42
match num:
    0:
        do_the_zero_routine()
        print("Zero")
    23:
        print("Half way there")
    42:
        print("The answer")
        make_it_sparkle()

@vnen Да, конечно!

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

Просто подбрасываю сюда идею.

В Haskell можно добавить защиту к каждому шаблону, например:

case [1, 3, 3, 7] of
    [1, a, b, 7] | a * 10 + b < 42 -> "Ok, passed the test"
                 | otherwise       -> "That is unacceptable!" -- otherwise = True
    _                              -> "I won't inspect you! >=D"

Возможный синтаксис GDScript может быть примерно таким:

match [1, 3, 3, 7]:
    [1, var a, var b, 7]
        if a * 10 + b < 42: print("Ok, passed the test")
    [1, var a, var b, 7]:   print("That is unacceptable!")
    _:                      print("I won't inspect you! >=D")

Мне кажется, что if более питонский, но он добавляет намного больше шума в код.
Также при выборе маршрута if я не знаю, следует ли реализовать случай otherwise . Падение тоже работает, как в примере.

@Zylann Я думаю, что это намного понятнее и легче для чтения, чем несколько if s. Но конечно, это не _необходимо_, но я думаю, что это было бы отличным дополнением к языку, потому что им действительно приятно пользоваться. Но это, конечно, субъективно.

Стоимость производительности не должна быть такой высокой. Я бы преобразовал эти вложенные шаблоны в несколько простых совпадений шаблонов. Если шаблон не соответствует при первой проверке, проверяется следующий случай, поэтому он будет не намного медленнее, чем if s.

Что ж, я согласен, что следует избегать штрафов за производительность. Я не уверен, будет ли этот синтаксис более дорогостоящим, чем обычная цепочка if . Это во многом зависит от реализации.

@vnen относительно реализации, я хочу реализовать ее, как описано в этой книге. (Реализация функциональных языков программирования Саймоном Пейтоном Джонсом)

edit: Я не буду реализовывать это таким образом, потому что GDscript - это язык с динамической типизацией, и выполнение подобной компиляции потребует излишне сложных преобразований и проверок во время выполнения.

Пример перевода из

match array:
    [1, 3, var a]: print(a)
    _:             print("No match")

к

if typeof(array) == TYPE_ARRAY:
    if array[0] == 1:
        if array[1] == 3:
            var a = array[2]
            if array.size() == 3:
                print(a)
else:
    var n = array
    print("No match")

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

@karroffel ну, меня это сбивает с толку. Не то чтобы его чтение сбивало с толку, но со всеми текущими функциями GDScript он выглядит как инопланетянин, и я не вижу, где это можно часто использовать. Я никогда не работал с функциональными языками вроде Haskell, кстати: s

@Zylann его можно использовать везде, где можно использовать переключатель. Просто прочтите это как оператор switch: wink:

Я понимаю отсутствие «необходимости» в этом, но тогда я могу возразить, что циклы for не нужны, потому что есть if -выражения и рекурсия: смеется:

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

var inventory = ["sword", "shield", "magic key"]
match inventory:
    ["sword", "shield", "magic key"]:
        print("You came as a pure warrior, Welcome!")
        can_open_door = true
        break;
    ["sword", "shield", ..]:
        can_kill_monster = true
        can_broke_door = true
        can_evade_monster = true
        break;
    ["sword", .. ]:
        can_broke_door = true
        break;
    ["shield", .. ]:
        can_evade_monster = true
        break;

@karroffel Будет ли это возможно?
Будет ли реализован формат отрицательного шаблона, чтобы ".." не совпадал с "волшебным ключом" для последних трех случаев? Или операторы «если» внутри этих блоков case лучше подходят для этой ситуации.

Должно ли это сопоставление с образцом быть таким же полнофункциональным, как регулярные выражения? : D (не спрашиваю, некоторые из них просто похожи)

Для меня наличие отрицательного шаблона (а не регулярного выражения) будет вариантом использования, потому что это сделает кодирование сценария простым и читаемым. Но если результат будет меньше этого, я бы предпочел обычные операторы «switch» и «if», если только нет побочного преимущества, такого как увеличение производительности или меньшее количество LoC.

Примечание: может быть и больше этого тоже не сработает. :)

Массивы @hubbyist зависят от порядка, поэтому ваш пример не будет работать таким образом. .. существует только для того, чтобы игнорировать остальную часть массива и сопоставлять ее (потому что это зависит от порядка, и вас не волнует остальное, например, только первые несколько элементов). Словари не имеют определенного порядка, возможно, я мог бы добавить специальный синтаксис, чтобы просто сопоставить ключи, чтобы использовать его как обычный набор.
О, и вам не понадобится break потому что нет провалов

@Zylann Нет, он будет сравнивать только переменные равенства и связывания, и все это может быть вложено.

Вчера реализовал парсер. Я добавил "набор синтаксиса" для словарей, чтобы проверить наличие ключа.

var inventory = {"sword": 20, "shield": 100, "magic key": 2}
match inventory:
    {"sword", "shield", "magic key"}:
        print("You came as a pure warrior, Welcome!")
        can_open_door = true
    {"sword", "shield", ..}:
        can_kill_monster = true
        can_broke_door = true
        can_evade_monster = true
    {"sword", .. }:
        can_broke_door = true
    {"shield", .. }:
        can_evade_monster = true

Итак, в этом примере мне не важны значения, я просто хочу проверить, присутствует ли свойство.

Также можно использовать простые константные выражения в качестве шаблона, например:

var size = 1024 * 1024 * 1024 # GiB

match size:
    1024:
        print("KiB")
    1024 * 1024:
        print("MiB")
    1024 * 1024 * 1024:
        print("GiB")
    _:
        print("something different")

Сегодня я преобразовываю это в if s и var s, и тогда большая часть тяжелой работы будет сделана.

Я бы хотел, чтобы это было реализовано. Я спрашиваю о переключателях каждые 3-6 месяцев :)

@karroffel По поводу охранников, pattern if cond звучит неплохо: smile:
(Я говорю о таких случаях, как

var arr = [1,2,3]
var first_required_value = 1
match arr:
  [x, ..] if x == first_required_value:
    foo(bar)
  a:
    printerr("Bad luck")

)

Будет ли разрешено и else/elif ?

Сначала: сопоставление констант уже завершено, так что в основном переключатель (он наконец-то здесь !!), теперь мне нужно работать над сопоставлением массивов и словарей ...

Я пока не знаю, как эффективно реализовать провал. Чтобы максимизировать эффективность, я хочу отсортировать шаблоны, поэтому общий «провал» может быть проблематичным. (если я не сортирую, мне, возможно, придется проверять содержимое массива несколько раз)

@Zylann Мне все еще нужно подумать о том, как реализовать охрану. else должно быть разрешено, если охранники разрешены ИМО, но также должен быть провал, если нет else и другие охранники терпят неудачу, так что сейчас это немного проблематично.

Что-то вроде стиля try-catch может быть лучше, чем провал IMO.

var inventory = {"sword": 20, "shield": 100, "magic key": 2}
match inventory:
    {"sword", "shield", "magic key"}:
        print("You came as a pure warrior, Welcome!")
        can_open_door = true
    ...
    ...
    ...
    {"shield", .. }:
        can_evade_monster = true
nomatch:
    print("You can't do anything here!")

Он будет более читабельным и при необходимости может быть объединен с другим оператором сопоставления или вызовом функции.

Я думаю, что при отсутствии совпадений else было бы более читаемым (хотя не уверен, должно ли оно быть внутри или вне блока).

Что о;

match x:
    ...
    ...
otherwise:
    ...

синтаксис?

Использование else похоже на перегрузку ключевого слова.

Обычно при сопоставлении с образцом используются:

_ = соответствует чему угодно, если совпадения нет ("шаблон подстановки"). Не привязывает идентификатор.

Это согласовано на всех языках, которые я знаю: Haskell, Scala, Elm, Racket, F #, OCaml, Rust. Я не думаю, что есть исключение.

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

Между прочим, все эти языки обычно (не всегда, Scala не поддерживает iirc) реализуют какой-то шаблон Nothing , когда нет значения (что отличается от провала).

Итак, после обсуждения охранников в IRC (спасибо @ akien-mga) я думаю, что мы пришли к хорошему компромиссу.

Вместо добавления охранников можно использовать continue для проверки других шаблонов. Таким образом, охранники будут просто if/else s в коде ветвления, а отказ / падение будет выполняться через continue .
Это упрощает анализатор, а шаблоны немного легче читать.

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

Я проведу сопоставление по порядку, что может быть более неэффективным, но упрощает многие преобразования.

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

Пт, 7 октября 2016 г., в 9:12, Karroffel [email protected] написал:

Итак, после обсуждения охранников в IRC (спасибо @ akien-mga
https://github.com/akien-mga) Думаю, мы нашли хороший компромисс.

Вместо добавления охранников можно использовать continue для проверки
другие шаблоны. Таким образом, охранники будут простыми, если / elses в коде ветвления
и неудача / провал будет происходить через continue.
Это упрощает анализатор, а шаблоны немного легче читать.

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

Я проведу сопоставление по порядку, что может быть более неэффективным, но это
упрощает многие преобразования.

-
Вы получаете это, потому что подписаны на эту ветку.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6593#issuecomment -252236803,
или отключить поток
https://github.com/notifications/unsubscribe-auth/AF-Z24qABylU7GPi4UNXJ1myXyKsGcIdks5qxjcRgaJpZM4KFJl9
.

@reduz Понятно , но зачем отказываться от функций, которые не нужно использовать, они необязательны. Здесь есть все функции переключения, а сопоставление с образцом стало гораздо более распространенным явлением в языках программирования в целом.

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

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

наивный вопрос: нет

match size:
    1024:
        print("KiB")
    1024 * 1024:
        print("MiB")
    1024 * 1024 * 1024:
        print("GiB")
    a:
        print("something different")

такой же как :

switch size:
    case 1024 :
        print("KiB")
        # implicit break
    case 1024 * 1024 :
        print("MiB")
        # implicit break
    case 1024 * 1024 * 1024 :
        print("GiB")
        # implicit break
    case a :
        print("something different")
        # implicit break

???

Так разве это не просто вопрос добавления / замены ключевых слов и сохранения более мощного сопоставления с образцом?

@SuperUserNameMan Это то же самое. (но case a будет default )

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

при использовании этого для выполнения базового switch/case (без использования более мощных функций сопоставления с образцом) добавляет ли он дополнительных затрат по сравнению с эквивалентом if/elif/else :

if size == 1024 : print("KiB");
elif size == 1024 * 1024 : print("MiB");
elif size == 1024 * 1024 * 1024 : print("GiB");
else : print("something different");

?

генерирует ли он больше кодов операций?
код, стоящий за ним, такой же быстрый?

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

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

@karroffel : тогда я лично не вижу причин (я if/elif/else .

Таким образом, это будет просто вопрос замены / добавления традиционных switch/case/default ключевых слов, чтобы он выглядел более «знакомым» и более «дружелюбным».

(кстати, возможно, ключевое слово case можно сделать необязательным, а switch можно сделать псевдонимом match ... таким образом, все будут счастливы)

(кстати, может быть, ключевое слово case можно сделать необязательным, а переключатель можно сделать псевдонимом соответствия ... таким образом, все будут счастливы)

Нет, попытка сделать всех счастливыми таким образом ведет к раздуванию, ужасному синтаксису и UX. Найдите лучший синтаксис и обеспечьте его соблюдение.

В "обычных" операторах switch должен быть break чтобы избежать провала, который не является идиоматическим стилем сопоставления _pattern_. Таким образом, смешивание этих синтаксисов потребует учета того, какой синтаксис использовался, и код должен быть сгенерирован по-другому. Я хотел бы иметь единый синтаксис

Я хотел бы предложить использовать синтаксис соответствия. Не было бы обязательного break и даже в операторах switch провал очень редок.

Все, что меняется, это:

  • изменить switch на match
  • drop case и break
  • для провала используйте continue

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

Использование break в switch es в первую очередь для меня не интуитивно

Лично мне не нравится писать ключевое слово break в операторах switch. В основном это лишняя строка кода, которая почти не нужна. Если начинается другой случай, это явный признак того, что предыдущий завершился. Падение просто работает для объединения нескольких вариантов в одну функцию, и только это делает разрыв полным. Но это можно сделать, объединив пустые случаи в один случай с ключевым словом or например, используя метод switch(true): или используя ключевое слово continue как предлагает @karroffel.

continue - это программный способ сделать это. Я не вижу причин не реализовывать в шаблонах что-то вроде 1, 2, 3 или даже 1 .. 3 . Да, это увеличило бы сложность, но также и обязательный перерыв.

Можно ли воспроизвести использование этого переключателя с сопоставлением с образцом? (псевдокод)

switch(val):
    case 0:
        return false
    case 1:
    case 2:
    case 3:
        print("Stuff")
        break
    case 4:
        print("42")
        break
    default:
        print("Default")

@Zylann

match val:
    0: return false
    var x:
        if x >= 1 && x <= 3:
            print("Stuff")
        else:
            continue
    4: print("42")
    _: print("Default")

а может я добавлю что-то вроде этого

match val:
    0: return false
    1 .. 3: print("Stuff")
    # different version
    # 1, 2, 3: print("Stuff")
    4: print("42")
    _: print("Default")

Что, если _ - существующая переменная? В конце концов, это правильное имя :)

@Zylann _ - это своего рода узор, а не привязка имени. Если бы это было имя, оно бы затеняло другую переменную, но поскольку это всего лишь шаблон, ничего особенного не происходит, и старый _ все еще можно использовать

Что произойдет, если вы поставите _: не в качестве последнего совпадения? Должен ли он выдать ошибку?

@Zylann Ну, он соответствует всему, поэтому все после этого не будет выполнено, за исключением случаев, когда вы помещаете continue внутри ветки.

Таким образом, он не должен выдавать ошибку, потому что вы можете продолжить выполнение с continue

Что касается конструкции 1..3 , я предпочитаю, безусловно, x,y,z one.

Обоснование:

  • match 1..3 неоднозначно. Соответствую ли я полному шаблону или одному из членов ранжирования по жанрам? Что, если я действительно хочу сопоставить массив, который выглядит как [1,2,3] ?
  • Если вам нужно сопоставить несколько значений, они не всегда (я бы сказал, вероятно, почти никогда) не будут последовательными. Рассмотреть возможность:
match http_response:
    200: "data loaded successfully"
    403,404,500: "an error occurred"

или

match tile_at(x,y):
    TILE_WATER:
        drown()
    TILE_WALL,TILE_BARREL,TILE_CHAR,TILE_SPIKE:
        stop()
        continue
    TILE_SPIKE:
        life = life -2

Не связано, но что касается реализаций в стране грез, мне нравится, что в некоторых языках совпадение - это выражение, поэтому вы можете сделать что-то вроде:

var acceleration = match input:
    UP: 
        vector(1,0)
    DOWN:
        vector(-1,0)

Не защищая этого, поскольку синтаксис типа пробелов GD делает его громоздким, просто подумал, что это интересная вещь :)

Для людей, выступающих за switch против match . Я, честно говоря, не вижу для этого веской рациональной причины

Это будет немного долго, не потому, что я так сильно забочусь об этом (я, вероятно, почти никогда сам не буду использовать switch из match ), а потому, что мне нужен конво быть более конструктивным, а не теряться в путанице.

Вот аргументы против match , из того, что я могу собрать:

  • у матча нет провалов
  • переключатель более знакомый

Начнем с аргумента 1:


match делает все, что делает switch, за исключением провала по умолчанию.
Однако по умолчанию падение составляет:

  1. практически никогда не использовался (за 20 лет программирования я ни разу не видел программы, в которой переключатель был бы реализован без break после каждого case . Я уверен, что он существует, но никогда не видел его )
  2. Вызывает ошибки (легко забыть, и компилятор не выдает ошибок)
  3. Если когда-либо использовалось, исключение, а не общий вариант использования (в этом случае более разумным является решение проблемы с помощью continue ).

TL; DR : match делает все, что switch делает хорошо, избегая ловушек switch , _and_ допускает более глубокую семантику, если программист желает их использовать (не в случае с switch , который менее гибкий, чем обычный ifs )


Аргумент в пользу знакомства также ошибочен, поскольку GD - это собственный язык, и не должно возникнуть проблем с изучением одного нового слова.
В качестве альтернативы аргумент знакомства должен быть расширен до: пробелов в качестве синтаксиса, elif , без фигурных скобок, один файл = один класс, ... Все это может быть или не быть знакомым, в зависимости от того, на каком языке вы использовал раньше. Например, для меня ничего из вышеперечисленного не подходит, а вот match подходит.
В Python даже нет switch (и не зря), поэтому даже обращение к синтаксической близости GD с Python не является допустимым аргументом.

Но, если предположить, что переключатель «знаком» (что, опять же, действительно зависит от того, с какого языка вы пришли, но допустим, что это так, ради аргумента), для программиста, ищущего документ и заменяющего

switch val:
    case a: 
        do_something()
        break;
    case b: 
        do_something_else()
        break;

с участием:

match val:
    a: 
        do_something()
    b: 
        do_something_else()

... банально.
Практически нет никакой кривой обучения. Для того, кто читает код, так же легко понять, что происходит, даже если вы никогда раньше не видели match . Кстати, именно так я узнал о конструкции match : читал код и натыкался на него. Мне не потребовалось больше доли секунды, чтобы понять, что это всего лишь чистильщик switch без break s и case s. Конечно, для понимания того, насколько мощно это было необходимо для чтения документации, но для понимания этого достаточно, чтобы использовать его везде, где можно было бы использовать switch , требуется от 1 до 10 секунд обучения.

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

TL, DR : аргумент о знакомстве не работает; Во-первых, это субъективно. Во-вторых, даже если предположить, что это правда, для программиста изучение немного другого синтаксиса является нормой для курса, а для непрограммистов совпадение с меньшей вероятностью будет ошибочным, но его немного легче изучить (тот же синтаксис минус одна конструкция ).


Таким образом, это предложение по улучшению языка:

  • доказано, что он семантически эффективен и представляет собой ценную абстракцию (что продемонстрировано всеми языками, которые его реализовали)
  • легче учиться и более защищено от ошибок для новичков, без каких-либо скрытых вещей, на которые нужно обратить внимание
  • позволяет использовать более мощные абстракции, чем его альтернатива
  • ... при этом не только не добавляя раздувания, но и удаляя некоторые
  • не создает дополнительной когнитивной нагрузки на программиста (не более чем запоминание elif вместо else if )

Я думаю, что можно использовать константы в качестве шаблона (для сопоставления с такими вещами, как TYPE_FLOAT или около того). Проблема в том, что во время синтаксического анализа (где я выполняю преобразования) невозможно определить, называет ли StringName константу или нет.

Таким образом, чтобы различать константы и привязки, необходимо использовать var name для привязки и просто SOME_CONSTANT для констант. (Я обновлю примеры в своем первоначальном комментарии)

Для сопоставления шаблонов разных типов необходимо добавить простую проверку типов, чтобы избежать ошибок во время выполнения. Чтобы не усложнять задачу, я просто хочу делать такие вещи (на самом деле сейчас я делаю это так):

match 4:
    "hello": print("It's a string that says \"hello\"")
    4:       print("It's 4")

# compiles to something like this
var match_value = 4 # actually it's "#match_value"
                    # this name can only be created by the compiler
                    # but this messes up syntax highlighting ;)
if typeof(match_value) == typeof("hello") && match_value == "hello":
    print("It's a string that says \"hello\"")
    # goto end
if typeof(match_value) == typeof(4) && match_value == 4:
    print("It's 4")
    # goto end
# end:

Если вы попытаетесь сопоставить шаблоны, которые имеют побочные эффекты, шаблон будет выполняться _twice_ (из-за проверки типов), поэтому было бы нехорошо использовать шаблоны с побочными эффектами. Я мог бы обойти это, но это потребовало бы учета временных переменных, которые только усложнили бы всю трансформацию.

Поэтому я бы сказал, что следует избегать паттернов с побочными эффектами, но не запрещать их. Что вы думаете?

Что такое паттерн с побочными эффектами? Вызов функции в шаблоне?

match val:
    get_some_int(val): some_effect()
    get_some_other_int(val): some_other_effect()

...Что?

И да, я думаю, что использование var для объявления переменных в шаблоне менее запутанно и более уместно.

цитата из: @Xananax

Я думаю, что использование var для объявления переменных в шаблоне менее запутанно и более уместно.

: +1:

@Xananax нет, что-то вроде этого:

var counter = 0
func side_effect_pls(x):
    counter += x
    if counter > 1:
        return x
    return counter

...
match 3:
    side_effect_pls(3): print("<- that's baaad")

Да, я это имел в виду.

Я думаю, что это может быть полезно (возможность вызывать функции в шаблоне), но если из-за этого реализация становится слишком сложной, то это того не стоит. Его всегда можно реализовать позже, если люди об этом попросят.

«Не кодируйте для среднего случая, не кодируйте для лучшего или худшего случая, кодируйте для наиболее часто используемого случая и останавливайтесь на этом» - какой-то чувак, я не помню, кто.

Должен следовать принципу @reduz : «Добавляйте минимум, необходимый для работы. Добавляйте больше, только если многие люди об этом просят».

Как вы собираетесь подсчитывать людей, которые его просят? Сколько это много?

Пальцами. Если не влезет в одну руку, наверное, много.

Есть ли причина, по которой вам нужно использовать подчеркивание для подстановочного знака провала ? Это исключает возможность использования его в качестве переменной

@ClikCode
Провалиться? Прохождение выполняется с помощью continue . Вы имеете в виду шаблоны с подстановочными знаками.

Цитата Xananax:

Это согласовано на всех языках, которые я знаю: Haskell, Scala, Elm, Racket, F #, OCaml, Rust. Я не думаю, что есть исключение.

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

@karroffel Все эти языки функциональны или сильно функциональны. А как насчет императивных языков?

@ bojidar-bg в большинстве императивных языков нет сопоставления с образцом, поэтому привести пример невозможно.

В C # 7 будет некоторая форма сопоставления с образцом, но это просто дополнение к базовому switch , поэтому здесь это бесполезно.

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

~матч бла:1: print («Я - кто-то»)еще:print ("Я никто")~

Как бы это сделал питон, если бы они когда-либо сделали это

@ClikCode а что насчет

match x:
    [1, _, _, 7]: print("kek")

[1, else, else, 7] не особо приятен для глаз.

как насчет использования '?' или "*" вместо символа "_"? Они используются в качестве подстановочных знаков для регулярных выражений, чтобы не сильно отличаться от сопоставления с образцом. Это оставит допустимые символы имени переменной вне синтаксиса сопоставления с образцом.

Что случилось с «переменными должны иметь описательные имена»? Есть ли веская причина, по которой кто-то захочет вызвать переменную _ если он не хочет, чтобы она была забыта ?

func (_,x) => x+1

Неявное значение функции выше заключается в том, что первый аргумент пропускается (даже не читая его тело). Независимо от сопоставления с образцом, я сомневаюсь, что вы можете обосновать свое желание вызвать переменную _ , если , опять же, вы не хотите ее использовать.

«Подчеркивание» библиотеки javascript называется _ именно потому, что никто никогда не будет использовать его в качестве имени переменной и поэтому является относительно «безопасным» глобальным.

За 20 лет работы с кодом с открытым исходным кодом в маленьких и больших компаниях я ни разу не видел, чтобы кто-то использовал _ в качестве имени переменной, если только они действительно не хотели, чтобы об этом забыли. Я не говорю, что мой анекдотический опыт является доказательством чего-либо, но это все же показатель.

Вот и «почему». Я понимаю, что это не убедительный аргумент, кто-то все еще может захотеть назвать переменную _ , но, тем не менее, рассмотрение наиболее распространенного случая важно.

Во-вторых; в некоторых языках (не знаю , если реализация Годо делает это), то _ делает решимость к тому , что был принят; Это полезная переменная, тогда как синтаксически невозможно использовать ? или * для того же эффекта.

например:

match val:
    0: print("it's a zero")
    4: print("it's a four")
    [_]: prints("it's an array containing",_)
    _: prints("it's a ",_)

Опять же, я не знаю, есть ли это у реализации Карроффеля (не во всех реализациях сопоставления есть), но независимо от этого; это полезное удобство и потенциально может быть добавлено позже. Это причина, по которой было выбрано подчеркивание, а не зарезервированное ключевое слово или недопустимый символ имени переменной.

Наконец; Использование и совместимость с общим синтаксисом превосходит это незначительное удобство. Для этого кодера со своим особым стилем, который любит использовать _ , мы бы:

  • раздражать каждого кодера, который знает сопоставление с образцом (потому что им придется помнить, что Годо - особый случай)
  • обучил бы каждого кодера, который не знает сопоставления с образцом, с вредными привычками (потому что им пришлось бы от этого отучиться)

@Xananax, спасибо, что _ - лучший выбор.

В настоящее время я просто игнорирую значение, но это будет лишь небольшое дополнение, которое сделает _ полезной переменной. Если об этом попросят, я добавлю это.

😄 если да, пожалуйста, сделайте _ полезной переменной

(проблема с тем, чтобы сделать его пригодным для использования идентификатором, состоит в том, что _ может встречаться в шаблоне несколько раз. Какое значение следует использовать? до обсуждения ...)

«Добавьте минимум, необходимый для работы. Добавляйте больше, только если многие люди об этом просят»

я цитирую внен цитирую редук

Похоже, мне придется вмешаться в это дело. Я действительно не ожидаю, что мое предложение найдет какую-либо поддержку, но все же есть некоторые вещи, о которых следует упомянуть.

@karroffel
Позвольте мне рассказать об удобстве использования Python по этой теме:

match foo:
    [_, _]: print("what ever")

можно так же легко заменить на

match foo:
    [*, *]: print("what ever")

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

match foo:
    [*, *]: print("what ever")

относительно использования _ в качестве значения, возвращаемого для подстановочного знака. Я считаю, что вы это уже реализовали.

match bar:
    var n: print("Unknown array structure: " + str(n))

так же легко может быть:

match bar:
    var _: print("Unknown array structure: " + str(_))

Единственная проблема в том, что это выглядит не так красиво. Но это по-прежнему помогает с удобочитаемостью, поскольку вы соблюдаете правила для всех допустимых имен переменных.


@Xananax
Думаю, вы как бы доказали мою точку зрения. библиотека underscore для javascript использует _ потому что она так редко используется для чего-либо еще. Но сейчас его используют, не так ли?

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


Относительно полезности подчеркивания как переменной, которая разрешает все, что было передано.
для этого шаблона [1, _, _, 2]:print(_) какой из них будет напечатан? Не то чтобы это было ясно. Попробуйте вместо этого [1, *, var _, 2]: print(_) и мы все знаем, какой из них будет напечатан, не так ли.


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

Вы говорите новичку: «Переменная - это имя для значения, она может использовать любую букву, цифру или символ подчеркивания, и она должна начинаться либо с буквы, либо с подчеркивания. Вы можете использовать это где угодно, кроме здесь. , здесь и здесь ".

С уважением, всем, кто нашел время послушать.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги