Как не забыть порядок параметров

Обсуждаются как существующие проекты (перевод документации, информационная система и т.п.), так и создание новых.

Модератор: Модераторы

Re: Как не забыть порядок параметров

Сообщение SSerge » 05.04.2014 07:54:41

sign Вам уже перечислили конкретную библиотеку и конкретную функцию, наименования параметров которой ни о чем не говорят, к тому же экземпляры для разных типов аргументов эти самые параметры именуют по-разному; что есть безусловное зло, но вам этого не понять, потому что вы изначально встали в позицию отрицания; Есть, впрочем, определенный тип программистов, который считает, что наименования параметров функций должны отражать только тип переменной и ничего более lpCStrA, lpCStrB (намекаю, откуда уши растут), а описания параметров должны содержаться только в закрытой документации, поставляемой за плату. Никаких там хелпов, справочных систем и т.д. Это, безусловно, самые прогрессивные профессионалы, потому что они заботятся о главном - чтобы как можно меньше было конкурентов, и все бабло с клиентуры по возможности принадлежало им. :D
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Как не забыть порядок параметров

Сообщение Дож » 05.04.2014 12:10:23

В fpc 2.7.1 можно делать так
Код: Выделить всё
{$mode objfpc}
{$modeswitch typehelpers}
{$modeswitch allowinline}
type
TStringUtils = type helper for String
  function IndexOf(const Sub: String): Integer; inline;
end;

function TStringUtils.IndexOf(const Sub: String): Integer;
begin
  Result := Pos(Sub, Self);
end;

begin
  Writeln('ABCDEF'.IndexOf('CD'));
end.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Как не забыть порядок параметров

Сообщение hinst » 05.04.2014 14:30:05

в общем я думаю что проблема с забытыми или недопонятыми параметрами функций не нова, и она вовсе не специфична для Pascal.
В C и C++ то же самое, особенно учитывая, что там в заголовке можно объявлять функции вообще не называя параметры, типа
Код: Выделить всё
float Power(float, float);

То же самое в Java, PHP, Haskell и прочих, перепутать параметры можно всегда, или вообще не написать документацию.
По этой причине я слышал что рекомендуют никогда не делать больше трёх параметров в функции, иначе легко запутаться.
Можно ввести языковую конструкцию для обозначения параметров, типа
Код: Выделить всё
Pos(substr = 'lawl', str = 'aw');

Так сделано в некоторых языках, вроде бы C#, R-project, больше не помню. В C# это опционально, в R-project - там вообще как-то непонятно.
Можно делать как я предложил, использовать этот паттерн, когда вызовы функций возвращают тот же самый объект.
Код: Выделить всё
result := TPos.Create('lawl').SetSubStr('aw').Eval;

Можно сделать либо object, либо class. В случае с class в Eval надо вызвать Free; в случае с object надо писать ^ такие штучки, либо сделать чтобы весь object каждый раз копировался, тогда штучки писать не придётся, но весь объект будет, вероятно, каждый раз копироваться, и если параметров много, то начнёт тормозить.
Я выбрал функцию Pos просто для примера, если уже помнишь, что в Pos первый параметр - подстрока, то можно и не изощряться здесь. Я это помню, прикол, однако, в том, что если я что-то помню или знаю, я вовсе не всегда так делаю, и не обязательно вспомню это в нужный момент, перепутать можно всё что угодно, во всяком случае я так могу, даже если знаешь таблицу умножения, всё равно можешь посчитать 7 * 8 = 58, особенно если торопишься, и о том, что делаешь, особенно не задумываешься, хотя я давно помню, как там идут параметры, я перепутываю их регулярно, и потом подолгу думаю, что не так.
В примере выше можно сделать функцию, которая будет возвращать новый TPos, тогда можно будет заменить:
Код: Выделить всё
result := Pos('lawl').SetSubStr('aw').Eval;

или так
Код: Выделить всё
result := Pos.SetStr('lawl').SetSubStr('aw').Eval;

В общем, возможны всякие варианты
Код: Выделить всё
var
  pos: TPos;
begin
  pos.Str := 'lawl';
  pos.SubStr := 'aw';
  result := pos.Eval;

Ну или сделать всё таки фичу в FPC, уж не знаю, будет ли её кто-нибудь делать, и стоит ли её делать
Код: Выделить всё
result := Pos(substr: 'lawl', str: 'aw');

Я думаю, что фичу делать не надо, так как решение есть, которое я описал. Это я видел в жабе:
Код: Выделить всё
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

Называется pattern Builder, и используется он там немного для других целей, как я понял, для того, чтобы создать объект и сразу назначить ему некоторые значения для свойств
пример отсюда: http://habrahabr.ru/post/86252/

Добавлено спустя 3 минуты 8 секунд:
то есть, там предлагают это использовать для создания экземпляров классов и назначения им свойств, а не для того, чтобы использовать это как вызов функции с именоваными параметрами, как это предлагаю я
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 05.04.2014 18:07:31

hinst писал(а):Я думаю, что фичу делать не надо, так как решение есть, которое я описал.

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

hinst писал(а): Это я видел в жабе:
Код: Выделить всё
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

Называется pattern Builder, и используется он там немного для других целей, как я понял, для того, чтобы создать объект и сразу назначить ему некоторые значения для свойств
пример отсюда: http://habrahabr.ru/post/86252/

Добавлено спустя 3 минуты 8 секунд:
то есть, там предлагают это использовать для создания экземпляров классов и назначения им свойств, а не для того, чтобы использовать это как вызов функции с именоваными параметрами, как это предлагаю я

Использовать методологию одних языков другими есть величайшее зло. Очень похоже, на что этот "builder pattern" пришёл в бедную яву, из какого-нибудь функционального языка.
Технические недостатки buildpatterna:
* инфраструктура удваивается (кроме самого класса - нужно написать ещё один класс - конструктор)
* нет чёткого описания какие методы критичны при строительстве (ведь можно и забыть!). например:
Код: Выделить всё
var
  p: TPos;
begin
  p.SearchWhat('bla').search(); // а вот SearchWhere я не вызвал.

это не так страшно в случае поиска, но в случае примера хабра "наклеечка" может оказаться весьма кривой (не говоря о более серьёзных задачах)
* создание класса очень дорогая операция получается! (на каждый параметр 1 вызов метода!!) - это дорого и жирно.
* такая "фабрика" есть замаскированные глобальные переменные. Hinst - лично для тебя - подумай, сможешь ли ты использовать 1 фабрику, из двух разных потоков? ;) зато вызвать функцию напрямую сможешь.

Личное мнение. "BuildPattern" это вывернутый на изнанку "JavaBean Pattern"- использую терминологию хабра статьи. Только JavaBean надёжнее и понятнее. Автор статьи и переводчик должны вернуться к использованию лиспа или схемы (уж не знаю, откуда у них идеи растут).
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение hinst » 05.04.2014 21:24:29

Я знаю как ещё и сделать чтобы нельзя было пропустить нужный параметр:
Код: Выделить всё
type

  { TPos }

  TPos = class
  public
    What: string;
    Where: string;
  end;

  TPosArg = object
    o: TPos;
  end;

  { TPosFinal }

  TPosFinal = object(TPosArg)
    function Eval: Integer;
  end;

  { TPosWhat }

  TPosWhat = object(TPosArg)
    function SetWhat(const s: string): TPosFinal;
  end;

  { TPosWhere }

  TPosWhere = object(TPosArg)
    function SetWhere(const s: string): TPosWhat;
  end;

function Pos: TPosWhere;

...implementation...

{ TPosWhere }

function TPosWhere.SetWhere(const s: string): TPosWhat;
begin
  o.Where := s;
  result.o := o;
end;

{ TPosFinal }

function TPosFinal.Eval: Integer;
begin
  result := system.Pos(o.What, o.Where);
  o.Free;
end;

{ TPosWhat }

function TPosWhat.SetWhat(const s: string): TPosFinal;
begin
  o.What := s;
  result.o := o;
end;

{ NoClass }

function Pos: TPosWhere;
begin
  result.o := TPos.Create;
end;



Обращение:
Код: Выделить всё
begin
  WriteLN(
    Pos.SetWhere('lawl').SetWhat('aw').Eval
  );
end.


Единственная проблема - писать ещё больше, и в случае если есть необязательные параметры, то надо будет ещё подумать, как это лучше сделать.
Смысл здесь в том, что каждый объект - присваиватель возвращает объект определённого типа, и каждый раз можно вызвать только строго определённый метод, то есть
Код: Выделить всё
Pos.SetWhere('lawl').SetWhat('aw').Eval // можно
Pos.SetWhat('aw').Eval // нельзя
Pos.SetWhat('aw').SetWhere('lawl').Eval // нельзя

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

Минусы:
1. Много писать.
2. TPos это class, под него будет выделяться место в куче каждый раз. Под промежуточные объекты, однако, память выделяться должна только в стеке
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 06.04.2014 01:14:40

:D жесть. Вот паскалевский способ не забыть порядок
Код: Выделить всё
function FindSubInStr(const sub, s: string): Integer; {inline; - по желанию}
begin
  Result:=Pos(sub, s);
end;

имя функции тебе диктует и порядок параметров.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение sign » 07.04.2014 06:00:43

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

А я вам показал, что за малое, совсем малое усилие, которое вообще ничего не стоит, человек тут же вспомнит, каков порядок параметров в данной функции.

hinst писал(а):Я знаю как ещё и сделать чтобы нельзя было пропустить нужный параметр:

Столько усилий, вместо простого Ctrl-пробел и Ctrl-Shift-пробел.
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Re: Как не забыть порядок параметров

Сообщение hinst » 07.04.2014 11:46:28

не всё так просто
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Как не забыть порядок параметров

Сообщение sign » 08.04.2014 10:40:09

hinst писал(а):не всё так просто

Ха!
При наличии интернета и массы литературы?
Мне, помнится, на кафедре, ещё в советское время, выдали дискету с прологом.
Описания ноль, только на бумажке несколько команд языка.
Разглядывая кишки, методом научного тыка, пролог таки был освоен.
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 08.04.2014 16:02:41

hinst писал(а):не всё так просто

Самый верный подход к разработке!
Выучи порядок параметров, будь мужиком, б :mrgreen: :mrgreen: :mrgreen: :mrgreen: ь!
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение Mirage » 08.04.2014 19:44:28

Выучивать - путь к ошибкам.
Городить нелепые конструкции - туда же.
Проблема решается вменяемыми практиками кодирования и средствами разработки.
IDE должна уметь показать названия аргументов, даже если нет исходников (в .ppu файле эта информация есть). А если есть исходники, то и весь комментарий к функции.
Названия аргументов, в свою очередь, всегда должны быть такими, чтобы по ним было понятно что надо передавать.

P.S.: На builder паттерн наехали зря, т.к. он нужен и удобен для безопасного создания immutable объектов, когда у объекта имеются необязательные параметры. Обязательные задаются при создании builder'а, читаем внимательней. А функциональные языки тут вообще не причем.
Было бы очень здорово, если бы immutable объекты давали те же гарантии в FPC, что и в Java.
Но т.к. иммутабельных объектов в FPC нет, то данный паттерн можно реализовать без дополнительного класса, просто сделав чтобы методы-сеттеры дополнительных параметров возвращали экземпляр того же класса. Это модно называть fluent interfaces. Толку, правда, от этого мало.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Как не забыть порядок параметров

Сообщение GrayEddy » 08.04.2014 21:46:11

А как насчет синонимов?
Примерно так (в идеале).
Код: Выделить всё
function Pos(sub, s: string) as FindSubInStr(const sub, s: string): Integer;

и вызывать в коде FindSubInStr(...)
GrayEddy
постоялец
 
Сообщения: 375
Зарегистрирован: 06.05.2005 09:37:56

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 08.04.2014 22:07:35

Mirage писал(а):Выучивать - путь к ошибкам.

....ээээ, никогда бы не подумал, что "выучивание" это путь к ошибкам. Скорее наоборот :) Невыучивание чего угодно, ведёт к непредасказуемым результатам.

Mirage писал(а):P.S.: На builder паттерн наехали зря, т.к. он нужен и удобен для безопасного создания immutable объектов, когда у объекта имеются необязательные параметры.

Не зря. Я уже привёл технические проблемы.
Сам такой билдер является надстройкой над самим классом, повторяющим интерфейс класса.
Никаких выгод он не несёт совершенно.

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

Mirage писал(а):Обязательные задаются при создании builder'а, читаем внимательней.

Очёнь точно подмечено!
А если обязательных ни два и не три параметра? А если опять человек не помнить какие-там параметры и в какой последовательности передовать? А сколько конструкторов должно быть у билдера?
Тогда зачем "билдер" вообще нужен, если проще создать сам объект напрямую.
Или делать "билдер" для "билдера"?
Инфраструктура может очень сильно разрастись, что очень неудобно.

Mirage писал(а):Было бы очень здорово, если бы immutable объекты давали те же гарантии в FPC, что и в Java.
Но т.к. иммутабельных объектов в FPC нет, то данный паттерн можно реализовать без дополнительного класса, просто сделав чтобы методы-сеттеры дополнительных параметров возвращали экземпляр того же класса.

Будет ли уместным спросить - зачем immutable объекты? Самое критичное место (строки) в паскале разрулено, использованием счётчиков.

Mirage писал(а):Это модно называть fluent interfaces. Толку, правда, от этого мало.

Соглашусь, что "толку мало" :)
Слышу этот термин в первый раз!
сама формулировка
Wikipedia писал(а):...нацеленный на повышение читабельности исходного кода программы....

вызывает сомнения в способности людей писатьадекватные (читать?) API, ну и сама читабельность Cи-подобных синтаксисов ставится под соменение.
Тут стоит заметить, как определить что является "читабельным", а что нет?! (то что читабельно для опытного программиста, новичка может поставить в тупик).

Вообще, паскаль исконно критикуют за то, что, по сравнению, с Си языками в нём слишком много нужно печатать. Я вижу исключительно обратную тендецию :D
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение stanilar » 09.04.2014 09:53:57

Касательно сути исходного вопроса - ну не умеет IDE читать мысли, не научили еще. Пока еще нужно самому напрягаться.
stanilar
постоялец
 
Сообщения: 289
Зарегистрирован: 09.03.2010 19:09:02

Re: Как не забыть порядок параметров

Сообщение hinst » 09.04.2014 14:06:34

Между прочим есть такой прикол, что в FPC такая похожая фигня, ну почти такая же, есть уже:
Код: Выделить всё
program project1;

{$Mode ObjFPC}

type
  TLal = record
    x, y: Integer;
  end;

const
  lal: TLal = (x: 0; y: 0);

var
  lal2: TLal;

begin
  lal2 := lal;
end.

Эта прога скомпилируется.
Обратите внимание на вот этот синтаксис:
lal: TLal = (x: 0; y: 0);

Было бы логично расширить его и на присваивание не-констант:
Код: Выделить всё
begin
  lal2 := (x: 0; y: 0);
  // или так
  lal2 := TLal(x: 0; y: 0);
end.

Но этого не сделано. Такая прога не скомпилируется. А если бы такое было, то можно было бы делать типа именованые параметры в record'е:
Код: Выделить всё
Pos( TPos(sub: '23'; str: '1234') );

Но этого
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Пред.След.

Вернуться в Разное

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

Рейтинг@Mail.ru