Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Страница 1 из 2 1 2 ПоследняяПоследняя
Показано с 1 по 10 из 13
  1. #1
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±

    switch-выражения (?)

    Внимание: В данной статье сравниваются с Pawn и критикуются некоторые черты языков Java и C#.
    Если вы очень любите C# или Java - возможно, вы захотите закрыть эту страницу. Я вас предупредил.

    Всем привет.

    Ещё летом прошлого года я копался в моде NGRP, проверяя правильность работы warning 210 и ещё пары других экспериментальных фич для компилятора, и случайно наткнулся на следующий код:
    1. // Файл: acceptcancel.pwn
    2. // Примечание: это код из работы механика. В зависимости от уровня своих умений (скилла)
    3. // механик может заправить машину только на определённое количество литров.
    4. new Float: fueltogive;
    5. switch(PlayerInfo[playerid][pMechSkill])
    6. {
    7. case 0 .. 49: fueltogive = 2.0;
    8. case 50 .. 99: fueltogive = 4.0;
    9. case 100 .. 199: fueltogive = 6.0;
    10. case 200 .. 399: fueltogive = 8.0;
    11. default: fueltogive = 10.0;
    12. }

    Выглядит немного раздуто, не так ли? Во всех ветках case происходит присвоение значения к одной и той же переменной, отличается только само присваеваемое значение.
    При взгляде на такой код люди, знакомые с Java и C#, наверняка вспомнят недавнее новшество этих языков - switch-выражения.
    Если вкратце, то эта фича позволяет использовать оператор switch внутри выражений, тем самым упрощая повседневную жизнь для программистов.
    А что, если в Pawn попробовать осуществить что-то подобное?



    Подход в стиле Java

    С Java-подобными switch-выражениями приведённый выше пример можно было бы переписать примерно так:
    1. new Float: fueltogive = switch(PlayerInfo[playerid][pMechSkill])
    2. {
    3. case 0 .. 49 -> 2.0;
    4. case 50 .. 99 -> 4.0;
    5. case 100 .. 199 -> 6.0;
    6. case 200 .. 399 -> 8.0;
    7. default -> 10.0;
    8. };

    Сразу бросаются в глаза следующие изъяны:
    • Фигурные скобки внутри выражения выглядят очень неестественно, т.к. обычно в Pawn их нельзя использовать внутри выражений.
    • В Pawn нет оператора "->". Если принять вариант с Java-подобным синтаксисом, придётся добавлять этот оператор как новый. Стоит ли оно того? Едва ли.
    • Синтаксис всё ещё выглядит раздутым из-за ключевых слов "case" и "default". Если у вас есть switch-выражение с небольшим количеством кейсов, у вас не получится так просто уместить его в одну-две строки.

    Вывод: не годится. Мало того, что такой синтаксис не удобен, так он ещё и совершенно не похож на Pawn.



    Подход в стиле C#

    В C# поступили немного грамотнее, догадавшись, что ключевые слова "case" и "default" могут мешаться при записи switch-выражений в одну строку, и поэтому заменили "default" на более лаконичный вариант "_", а от "case" и вовсе отказались.
    1. new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch
    2. {
    3. 0 .. 49 => 2.0,
    4. 50 .. 99 => 4.0,
    5. 100 .. 199 => 6.0,
    6. 200 .. 399 => 8.0,
    7. _ => 10.0
    8. };

    В Pawn оператор "_" уже можно использовать в вызовах функций, когда нужно пропустить какой-то аргумент (пример: "gettime(_, minutes, seconds)"), т.е. этот оператор уже означает "по умолчанию", благодаря чему использование "_" вместо "default" в switch-выражениях выглядит интуитивно понятным.
    Кроме того, с таким лаконичным синтаксисом уже есть смысл не расписывать каждый кейс на отдельной строке, и можно попробовать записать выражение покомпактнее:
    1. new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch {
    2. 0..49 => 2.0, 50..99 => 4.0, 100..199 => 6.0, 200..399 => 8.0, _ => 10.0
    3. };

    или даже так:
    1. new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch
    2. { 0..49 => 2.0, 50..99 => 4.0, 100..199 => 6.0, 200..399 => 8.0, _ => 10.0 };

    Но и этот стиль не лишён своих недостатков:
    • Фигурные скобки никуда не делись, и внутри выражения они всё так же не к месту.
    • Опять же, новый оператор "=>" вместо "->" из Java. Меняем шило на мыло.

    Вывод: есть небольшой прогресс в сравнении с Java-подобным вариантом, но для Pawn такой стиль всё ещё плохо подходит. Можно сделать и лучше.



    Собственный вариант?

    Не всё, что хорошо в языке A, укладывается в языке B, и те два стиля switch-выражений, рассмотренные выше - прекрасное тому доказательство.
    Но каким же тогда может быть если и не идеальный для большинства пользователей, то хотя бы приемлемый для реалий Pawn синтаксис?
    • Отличия от обычного ветвления switch допустимы, но в меру.
      Pawn - это, прежде всего, язык для новичков, поэтому не стоит излишне усложнять его синтаксис; switch-выражения должны всё ещё выглядеть и восприниматься как Pawn, а не как какой-то новый язык.
      Использование стрелок из Java и C# ("->" и "=>") вместо ":" мало того, что не укладывается в этот принцип, так ещё и не даёт абсолютно никакой выгоды.
      Фигурные скобки тоже можно заменить на круглые, в Pawn внутри выражений они будут выглядеть куда естественнее.
    • Лаконичность.
      В этом плане можно перенять идею из C# с отказом от "case" и заменой "default" на "_".
      Да, это будет заметным отличием от обычного switch, но и что с того? В Pawn же нельзя использовать if внутри выражений - вместо этого есть тернарные выражения. Примерно то же самое и здесь, ничего особенного.


    Моё мнение может отличаться от мнения большинства (а может и нет), но удобным и в то же время естественным в Pawn будет выглядеть такой синтаксис:
    1. new result = switch (x;
    2. 1..10: expr1;
    3. 11..30: expr2;
    4. 31..60: expr3;
    5. _: expr4;
    6. );

    В компактной записи:
    1. new result = switch(x; 1..10: expr1; 11..30: expr2; 31..60: expr3; _: expr4);

    Синтаксис отчасти позаимствован из цикла for, где тоже 3 выражения взяты в круглые скобки и отделяются друг от друга точкой с запятой. Иными словами, помимо скобок и отказа от case и default в синтаксисе ничего не изменено.

    Если же взять более ранний пример с работой механика, то его можно было бы записать примерно так:
    1. new Float: fueltogive = switch (PlayerInfo[playerid][pMechSkill];
    2. 0 .. 49: 2.0;
    3. 50 .. 99: 4.0;
    4. 100 .. 199: 6.0;
    5. 200 .. 399: 8.0;
    6. _: 10.0;
    7. );

    или так:
    1. new Float:fueltogive = switch (PlayerInfo[playerid][pMechSkill];
    2. 0..49: 2.0; 50..99: 4.0; 100..199: 6.0; 200..399: 8.0; _: 10.0);



    От слов к делу

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

    Тестовый билд: https://www.dropbox.com/s/39s6h80ho8...-expr.zip?dl=0

    Имейте в виду: этот билд предоставляется чисто в ознакомительных целях и не предназначен для повседневного использования.
    Ни в коем случае не пытайтесь всерьёз использовать switch-выражения в своём моде (и уж тем более в каком-нибудь публичном инклуде/моде/фильтрскрипте), т.к. эта фича ещё не включена в следующий релиз компилятора официально (и не факт, что вообще когда-нибудь туда попадёт).
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  2. 3 пользователя(ей) сказали cпасибо:
    DeimoS (05.01.2020) execution (06.01.2020) Kovshevoy (03.01.2020)
  3. #2
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Также очень хотелось бы знать мнение читателей по следующим вопросам:

    • Стоит ли вообще предлагать эту фичу к включению в компилятор?
      Ок, на самом деле здесь я на 99% уверен, что люди, управляющие репозиторием компилятора на GitHub, будут против и аргументируют это тем, что такая фича излишне усложняет язык Pawn.
      Но всё же было бы здорово узнать, насколько мнение сообщества отличается от мнения четырёх человек на GitHub.
      Стоит ли того упрощение кода с помощью switch-выражений, или же цена (усложнение синтаксиса языка) слишком велика?

    • Возможно, я выбрал не самый удачный синтаксис?
      Если так, то не стесняйтесь предложить свой вариант (при условии, что аргументируете свой выбор).
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  4. #3
    Аватар для Pa4enka
    Пользователь

    Статус
    Оффлайн
    Регистрация
    22.04.2016
    Адрес
    Украина
    Сообщений
    157
    Репутация:
    35 ±
    Хм, а чем старый вариант не устраивает(имеется ввиду вариант с мода NGRP)? Генерируются лишние инструкции присвоения?

     Синтаксис
    1. new Float: fueltogive = switch(PlayerInfo[playerid][pMechSkill]:
    2. 0 .. 49 = 2.0; 50 .. 99 = 4.0; 100 .. 199 = 6.0; 200 .. 399 = 8.0; _= 10.0
    3. );

    Таким образом, используя ":", интуитивно понятно, что мы отталкиваемся от такой-то переменной/массива и исходя от ее значения происходит присвоения какого-то числа. Кроме того, использования знака "=" для скриптера здесь наиболее логичней, нежели его отсутствия. Хотя все же такой синтаксис выглядит довольно смешно для павн, имхо. Но в целом, если от этой фичи будет какой-то профит, то привыкнуть будет не сложно к любому синтаксису.

  5. #4
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    45
    Репутация:
    9 ±
    Идея отличная. Еще можно if запихнуть, чтобы можно было нормально различать if-elseif-else вместо тернарного оператора. Мне этот синтаксис более приятен, чем старый.. вероятно это из-за разработке на расте. Можно не изменять синтаксис, а сделать отдельный оператор __switch или __match, который будет поддерживать свой собственный синтаксис.. Таким образом мы не нарушим стандарт, но и сделаем расширение в языке.

  6. #5
    Аватар для MassonNN
    Пользователь

    Статус
    Оффлайн
    Регистрация
    16.03.2018
    Адрес
    Москва
    Сообщений
    129
    Репутация:
    6 ±
    Код HTML:
    new result = switch (x): {0..5: = 3, 6..10: = 4, 11..20: = 5};
    1). Аналогичен стандартному.
    2). Похож в использовании на множественное присвоение:

    Код HTML:
    X=
    Y=
    Z= 0;
    3). Чисто для меня: лучше делать функционал использования не принципиально новым, и не делать из оператора функцию (как в последнем примере), а все таки оставлять его оператором.

  7. #6
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от vvw Посмотреть сообщение
    Еще можно if запихнуть, чтобы можно было нормально различать if-elseif-else вместо тернарного оператора. Мне этот синтаксис более приятен, чем старый.. вероятно это из-за разработке на расте.
    Вероятно.

    Цитата Сообщение от vvw Посмотреть сообщение
    Можно не изменять синтаксис, а сделать отдельный оператор __switch или __match, который будет поддерживать свой собственный синтаксис.. Таким образом мы не нарушим стандарт, но и сделаем расширение в языке.
    Не понимаю о чём ты. Зачем здесь "__switch", "__match" или ещё какое-то новое ключевое слово, если switch-выражения никаким боком не нарушают совместимость с уже существующим кодом на Pawn?



    Pa4enka, MassonNN, искренне не понимаю вашей логики. Ощущение, будто никто из вас не дочитал статью до конца, пропустив мимо ушей ключевой посыл:
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Pawn - это, прежде всего, язык для новичков, поэтому не стоит излишне усложнять его синтаксис; switch-выражения должны всё ещё выглядеть и восприниматься как Pawn, а не как какой-то новый язык.
    В своём варианте я отчасти взял за основу синтаксис цикла for, в котором тоже 3 выражения взяты в скобки и отделяются друг от друга с помощью точек с запятой (возможно, мне стоило сделать это пояснение сразу, я уже добавил его в статью). Иными словами, от стандартного синтаксиса отличий минимум.

    А теперь...
    Цитата Сообщение от Pa4enka Посмотреть сообщение
    1. new Float: fueltogive = switch(PlayerInfo[playerid][pMechSkill]:
    2. 0 .. 49 = 2.0; 50 .. 99 = 4.0; 100 .. 199 = 6.0; 200 .. 399 = 8.0; _= 10.0
    3. );
    Какое отношение такой синтаксис вообще имеет к Pawn?
    В чём профит от придания знаку "=" нового значения (отделение значений кейса от выражения)? Чем вместо этого не устроил знак ":"? Или знак "=" призван показать присваивание? Так ведь знак присваивания уже есть перед словом switch.


    Цитата Сообщение от MassonNN Посмотреть сообщение
    Код HTML:
    new result = switch (x): {0..5: = 3, 6..10: = 4, 11..20: = 5};
    См. выше. Не вижу смысла повторяться, тем более что всё это просто походит на попытку троллинга.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  8. #7
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    45
    Репутация:
    9 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Не понимаю о чём ты. Зачем здесь "__switch", "__match" или ещё какое-то новое ключевое слово, если switch-выражения никаким боком не нарушают совместимость с уже существующим кодом на Pawn?
    У думал, ты хочешь добавить доп функционал к оператору. Тогда извините.

    Кстати, ты открыл issue с этой фичей? Может быть там лучше обсудить синтаксис оператора?

  9. #8
    Аватар для MassonNN
    Пользователь

    Статус
    Оффлайн
    Регистрация
    16.03.2018
    Адрес
    Москва
    Сообщений
    129
    Репутация:
    6 ±
    Это ты меня не понял. Как раз таки я не добавил никакого новшества в switch, я напротив за этим и написал, что твой вариант менее похож на оригинальный:


    В развернутом виде это будет выглядеть так:

    1. new result = switch (x) {
    2. 0..5: = 3;
    3. 6..10: = 4;
    4. 11..20: = 5;
    5. }


    По поводу знака равно. Здесь я уже сказал, что это тоже напоминает множественное присвоение, какое есть в pawn




    Объясняю, почему мне не нравится твой вариант:

    Моё мнение может отличаться от мнения большинства (а может и нет), но удобным и в то же время естественным в Pawn будет выглядеть такой синтаксис:
    new result = switch (x;
    1..10: expr1;
    11..30: expr2;
    31..60: expr3;
    _: expr4;
    );
    Дело в том, что теперь switch выглядит как ФУНКЦИЯ, а не оператор. Не мне тебе объяснять в чем разница и почему логичнее было бы оставить синтаксис ОПЕРАТОРА)
    Последний раз редактировалось MassonNN; 07.01.2020 в 23:30.

  10. #9
    Аватар для vvw
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.08.2019
    Сообщений
    45
    Репутация:
    9 ±
    Цитата Сообщение от MassonNN Посмотреть сообщение
    Это ты меня не понял. Как раз таки я не добавил никакого новшества в switch, я напротив за этим и написал, что твой вариант менее похож на оригинальный:


    В развернутом виде это будет выглядеть так:

    1. new result = switch (x) {
    2. 0..5: = 3;
    3. 6..10: = 4;
    4. 11..20: = 5;
    5. }


    По поводу знака равно. Здесь я уже сказал, что это тоже напоминает множественное присвоение, какое есть в pawn




    Объясняю, почему мне не нравится твой вариант:



    Дело в том, что теперь switch выглядит как ФУНКЦИЯ, а не оператор. Не мне тебе объяснять в чем разница и почему логичнее было бы оставить синтаксис ОПЕРАТОРА)
    Языки всегда улучшаются, взаимстуют синтаксис из других языков, поэтому это нормально, что оператор может выглядеть как функция (кстати, почему как функция?). Сейчас мода пошла на взаимстование синтаксиса из ФП.

  11. #10
    Аватар для MassonNN
    Пользователь

    Статус
    Оффлайн
    Регистрация
    16.03.2018
    Адрес
    Москва
    Сообщений
    129
    Репутация:
    6 ±
    Цитата Сообщение от vvw Посмотреть сообщение
    Языки всегда улучшаются, взаимстуют синтаксис из других языков, поэтому это нормально, что оператор может выглядеть как функция (кстати, почему как функция?). Сейчас мода пошла на взаимстование синтаксиса из ФП.
    Я может не так выразился. Я имею ввиду оператор это оператор:

    Код HTML:
    if () {} else {}

    Код HTML:
    for(new i; i < i+1; i++) {}
    А функция это функция и думаю не нужно путать их синтаксис:

    Код HTML:
    SendClientMessage(playerid, 0x00110011FF, !"Cho");

    Просто для новичков думаю это усложнение и перемешка только усложнит понимание ЯП Pawn (которое создавалось как простое)

 

 
Страница 1 из 2 1 2 ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •