Daniel_Cortez
03.01.2020, 21:32
Внимание: В данной статье сравниваются с Pawn и критикуются некоторые черты языков Java и C#.
Если вы очень любите C# или Java - возможно, вы захотите закрыть эту страницу. Я вас предупредил.
Всем привет.
Ещё летом прошлого года я копался в моде NGRP (https://github.com/NextGenerationGamingLLC/SA-MP-Development), проверяя правильность работы warning 210 (https://pro-pawn.ru/showthread.php?16839-warning-210-%28-%29) и ещё пары других экспериментальных фич для компилятора, и случайно наткнулся на следующий код:
// Файл: acceptcancel.pwn
// Примечание: это код из работы механика. В зависимости от уровня своих умений (скилла)
// механик может заправить машину только на определённое количество литров.
new Float: fueltogive;
switch(PlayerInfo[playerid][pMechSkill])
{
case 0 .. 49: fueltogive = 2.0;
case 50 .. 99: fueltogive = 4.0;
case 100 .. 199: fueltogive = 6.0;
case 200 .. 399: fueltogive = 8.0;
default: fueltogive = 10.0;
}
Выглядит немного раздуто, не так ли? Во всех ветках case происходит присвоение значения к одной и той же переменной, отличается только само присваеваемое значение.
При взгляде на такой код люди, знакомые с Java и C#, наверняка вспомнят недавнее новшество этих языков - switch-выражения.
switch-выражения в Java (https://habr.com/ru/post/443464/)
switch-выражения в C# (https://docs.microsoft.com/ru-ru/dotnet/csharp/whats-new/csharp-8#switch-expressions)
Если вкратце, то эта фича позволяет использовать оператор switch внутри выражений, тем самым упрощая повседневную жизнь для программистов.
А что, если в Pawn попробовать осуществить что-то подобное?
Подход в стиле Java
С Java-подобными switch-выражениями приведённый выше пример можно было бы переписать примерно так:
new Float: fueltogive = switch(PlayerInfo[playerid][pMechSkill])
{
case 0 .. 49 -> 2.0;
case 50 .. 99 -> 4.0;
case 100 .. 199 -> 6.0;
case 200 .. 399 -> 8.0;
default -> 10.0;
};
Сразу бросаются в глаза следующие изъяны:
Фигурные скобки внутри выражения выглядят очень неестественно, т.к. обычно в Pawn их нельзя использовать внутри выражений.
В Pawn нет оператора "->". Если принять вариант с Java-подобным синтаксисом, придётся добавлять этот оператор как новый. Стоит ли оно того? Едва ли.
Синтаксис всё ещё выглядит раздутым из-за ключевых слов "case" и "default". Если у вас есть switch-выражение с небольшим количеством кейсов, у вас не получится так просто уместить его в одну-две строки.
Вывод: не годится. Мало того, что такой синтаксис не удобен, так он ещё и совершенно не похож на Pawn.
Подход в стиле C#
В C# поступили немного грамотнее, догадавшись, что ключевые слова "case" и "default" могут мешаться при записи switch-выражений в одну строку, и поэтому заменили "default" на более лаконичный вариант "_", а от "case" и вовсе отказались.
new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch
{
0 .. 49 => 2.0,
50 .. 99 => 4.0,
100 .. 199 => 6.0,
200 .. 399 => 8.0,
_ => 10.0
};
В Pawn оператор "_" уже можно использовать в вызовах функций, когда нужно пропустить какой-то аргумент (пример: "gettime(_, minutes, seconds)"), т.е. этот оператор уже означает "по умолчанию", благодаря чему использование "_" вместо "default" в switch-выражениях выглядит интуитивно понятным.
Кроме того, с таким лаконичным синтаксисом уже есть смысл не расписывать каждый кейс на отдельной строке, и можно попробовать записать выражение покомпактнее:
new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch {
0..49 => 2.0, 50..99 => 4.0, 100..199 => 6.0, 200..399 => 8.0, _ => 10.0
};
или даже так:
new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch
{ 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 будет выглядеть такой синтаксис:
new result = switch (x;
1..10: expr1;
11..30: expr2;
31..60: expr3;
_: expr4;
);
В компактной записи:
new result = switch(x; 1..10: expr1; 11..30: expr2; 31..60: expr3; _: expr4);
Синтаксис отчасти позаимствован из цикла for, где тоже 3 выражения взяты в круглые скобки и отделяются друг от друга точкой с запятой. Иными словами, помимо скобок и отказа от case и default в синтаксисе ничего не изменено.
Если же взять более ранний пример с работой механика, то его можно было бы записать примерно так:
new Float: fueltogive = switch (PlayerInfo[playerid][pMechSkill];
0 .. 49: 2.0;
50 .. 99: 4.0;
100 .. 199: 6.0;
200 .. 399: 8.0;
_: 10.0;
);
или так:
new Float:fueltogive = switch (PlayerInfo[playerid][pMechSkill];
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/39s6h80ho89k9vk/pawncc-3.10.9-switch-expr.zip?dl=0
Имейте в виду: этот билд предоставляется чисто в ознакомительных целях и не предназначен для повседневного использования.
Ни в коем случае не пытайтесь всерьёз использовать switch-выражения в своём моде (и уж тем более в каком-нибудь публичном инклуде/моде/фильтрскрипте), т.к. эта фича ещё не включена в следующий релиз компилятора официально (и не факт, что вообще когда-нибудь туда попадёт).
Если вы очень любите C# или Java - возможно, вы захотите закрыть эту страницу. Я вас предупредил.
Всем привет.
Ещё летом прошлого года я копался в моде NGRP (https://github.com/NextGenerationGamingLLC/SA-MP-Development), проверяя правильность работы warning 210 (https://pro-pawn.ru/showthread.php?16839-warning-210-%28-%29) и ещё пары других экспериментальных фич для компилятора, и случайно наткнулся на следующий код:
// Файл: acceptcancel.pwn
// Примечание: это код из работы механика. В зависимости от уровня своих умений (скилла)
// механик может заправить машину только на определённое количество литров.
new Float: fueltogive;
switch(PlayerInfo[playerid][pMechSkill])
{
case 0 .. 49: fueltogive = 2.0;
case 50 .. 99: fueltogive = 4.0;
case 100 .. 199: fueltogive = 6.0;
case 200 .. 399: fueltogive = 8.0;
default: fueltogive = 10.0;
}
Выглядит немного раздуто, не так ли? Во всех ветках case происходит присвоение значения к одной и той же переменной, отличается только само присваеваемое значение.
При взгляде на такой код люди, знакомые с Java и C#, наверняка вспомнят недавнее новшество этих языков - switch-выражения.
switch-выражения в Java (https://habr.com/ru/post/443464/)
switch-выражения в C# (https://docs.microsoft.com/ru-ru/dotnet/csharp/whats-new/csharp-8#switch-expressions)
Если вкратце, то эта фича позволяет использовать оператор switch внутри выражений, тем самым упрощая повседневную жизнь для программистов.
А что, если в Pawn попробовать осуществить что-то подобное?
Подход в стиле Java
С Java-подобными switch-выражениями приведённый выше пример можно было бы переписать примерно так:
new Float: fueltogive = switch(PlayerInfo[playerid][pMechSkill])
{
case 0 .. 49 -> 2.0;
case 50 .. 99 -> 4.0;
case 100 .. 199 -> 6.0;
case 200 .. 399 -> 8.0;
default -> 10.0;
};
Сразу бросаются в глаза следующие изъяны:
Фигурные скобки внутри выражения выглядят очень неестественно, т.к. обычно в Pawn их нельзя использовать внутри выражений.
В Pawn нет оператора "->". Если принять вариант с Java-подобным синтаксисом, придётся добавлять этот оператор как новый. Стоит ли оно того? Едва ли.
Синтаксис всё ещё выглядит раздутым из-за ключевых слов "case" и "default". Если у вас есть switch-выражение с небольшим количеством кейсов, у вас не получится так просто уместить его в одну-две строки.
Вывод: не годится. Мало того, что такой синтаксис не удобен, так он ещё и совершенно не похож на Pawn.
Подход в стиле C#
В C# поступили немного грамотнее, догадавшись, что ключевые слова "case" и "default" могут мешаться при записи switch-выражений в одну строку, и поэтому заменили "default" на более лаконичный вариант "_", а от "case" и вовсе отказались.
new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch
{
0 .. 49 => 2.0,
50 .. 99 => 4.0,
100 .. 199 => 6.0,
200 .. 399 => 8.0,
_ => 10.0
};
В Pawn оператор "_" уже можно использовать в вызовах функций, когда нужно пропустить какой-то аргумент (пример: "gettime(_, minutes, seconds)"), т.е. этот оператор уже означает "по умолчанию", благодаря чему использование "_" вместо "default" в switch-выражениях выглядит интуитивно понятным.
Кроме того, с таким лаконичным синтаксисом уже есть смысл не расписывать каждый кейс на отдельной строке, и можно попробовать записать выражение покомпактнее:
new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch {
0..49 => 2.0, 50..99 => 4.0, 100..199 => 6.0, 200..399 => 8.0, _ => 10.0
};
или даже так:
new Float: fueltogive = PlayerInfo[playerid][pMechSkill] switch
{ 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 будет выглядеть такой синтаксис:
new result = switch (x;
1..10: expr1;
11..30: expr2;
31..60: expr3;
_: expr4;
);
В компактной записи:
new result = switch(x; 1..10: expr1; 11..30: expr2; 31..60: expr3; _: expr4);
Синтаксис отчасти позаимствован из цикла for, где тоже 3 выражения взяты в круглые скобки и отделяются друг от друга точкой с запятой. Иными словами, помимо скобок и отказа от case и default в синтаксисе ничего не изменено.
Если же взять более ранний пример с работой механика, то его можно было бы записать примерно так:
new Float: fueltogive = switch (PlayerInfo[playerid][pMechSkill];
0 .. 49: 2.0;
50 .. 99: 4.0;
100 .. 199: 6.0;
200 .. 399: 8.0;
_: 10.0;
);
или так:
new Float:fueltogive = switch (PlayerInfo[playerid][pMechSkill];
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/39s6h80ho89k9vk/pawncc-3.10.9-switch-expr.zip?dl=0
Имейте в виду: этот билд предоставляется чисто в ознакомительных целях и не предназначен для повседневного использования.
Ни в коем случае не пытайтесь всерьёз использовать switch-выражения в своём моде (и уж тем более в каком-нибудь публичном инклуде/моде/фильтрскрипте), т.к. эта фича ещё не включена в следующий релиз компилятора официально (и не факт, что вообще когда-нибудь туда попадёт).