Nano Hash - криптовалюты, майнинг, программирование

Передайте необязательную логическую переменную

Иногда нам нужен необязательный параметр

function doSomething(foo:Integer; bar:TObject=nil)
begin
    if bar <> nil then // do something optional with bar
    ....
end

Как мне сделать эквивалент с логическим значением, которое позволяет мне различать два логических значения и «нет значения»?

function doSomething(foo:Integer; bar:Boolean=nil) // Won't compile
begin
    if bar <> nil then // do something optional with bar
end

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

По сути, мне нужен параметр с тремя возможными состояниями; истина, ложь или «не указано».

22.09.2011

Ответы:


1

Вы можете сделать это по-другому, используя перегрузку:

function doSomething(foo:Integer): Boolean; overload;
begin
  // do something without bar
end;

function doSomething(foo:Integer; bar:Boolean): Boolean; overload
begin
  // do something optional with bar
end;

Затем вы можете использовать его как doSomething(1) , а также doSomething(1, true)

Используя ваш пример, это будет эквивалентно:

function doSomething(foo:Integer; bar:Boolean=nil): Boolean; // Won't compile
begin
    if bar <> nil then 
      // do something optional with bar
    else
      // do something without bar
end;
22.09.2011
  • +1 Это лучший способ добиться этого, ИМО. Есть три состояния: doSomething(1), doSomething(1, True) и doSomething(1, False). 22.09.2011
  • @Rudy, да, но только потому, что в OP указано, что необходимо работать с булевыми параметрами. В противном случае я думаю, что 3 различных пути кода более эффективно различаются с помощью одной переменной с тремя состояниями (например, пример ThreeStateBoolean Грега Хьюгилла). 22.09.2011
  • @Rudy, что делает этот ответ лучшим, так это то, что он соответствует предложению OP, иногда нам нужен необязательный параметр, а не тот факт, что поддерживаются три состояния (потому что оба ответа делают это). ИМО. 22.09.2011
  • @Sam: конечно, это не только из-за трех состояний, а потому, что логическое значение не всегда требуется. Оба способа будут работать: ThreeStateBoolean или описанный выше. Мне больше нравится тот, что выше. 22.09.2011
  • Преимущество этого метода по сравнению с пользовательским типом ThreeStateBoolean заключается в том, что этот метод не требует дополнительных зависимостей. Было бы хорошо, если бы в основных библиотеках Delphi было что-то подобное. 23.09.2011

  • 2

    Значения типа Boolean могут быть только True или False. Вы можете определить свой собственный тип, который имеет три состояния: True, False и Unspecified:

    type ThreeStateBoolean = (True, False, Unspecified);
    

    Или вы можете передать указатель на логическое значение:

    type PBoolean = ^Boolean;
    
    function doSomething(bar: PBoolean = nil)
    begin
        if bar <> nil then
            // do something with bar^
    end
    

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

    22.09.2011
  • только первая часть вашего ответа верна, вы не можете объявить такую ​​​​функцию в delphi, вы должны использовать предопределенные типы в параметрах, таких как PBoolean , поэтому должно быть что-то вроде procedure doSomething2(bar: PBoolean = nil); 22.09.2011
  • Спасибо, я изменю это. Прошло очень много времени с тех пор, как я кодировал на Паскале... 22.09.2011
  • введите ThreeStateBoolean = (Истина, Ложь, Не указано); Обратите внимание, однако, что вы не можете фактически использовать True и False без перезаписи системных логических объявлений True и False. Это приведет к ошибкам компиляции везде, где вы действительно используете логические значения. TTrue, TFalse, Unspecified будут работать. 22.09.2011

  • 3

    Другой вариант (если у вас относительно современная версия Delphi) — реализовать это как запись с неявным преобразованием в логические значения и обратно. При перегрузке операторов вы также можете включить логику с тремя состояниями. Это излишество, если все, что вам нужно, это периодическое использование, но если вам нужна логическая система с тремя состояниями, она работает очень хорошо, особенно если вы можете присвоить ей логические значения. Будьте осторожны с заданиями от мысли с 3 состояниями к мысли с 2 состояниями. В приведенном ниже примере присваивается значение False логическому ‹- 'Troolean', где troolean — TNil, в соответствии с неназначенным логическим значением в Delphi, но есть очевидные сложности.

    Обратите внимание, что это никоим образом не является полной или эффективной реализацией, это просто демонстрация возможностей. Между прочим, есть хорошее видео CodeRage от Jeroen Pluimers о типах, допускающих значение NULL. Этот вопрос содержит ссылку.

    unit UnitTroolean;
    
    interface
    
    type
    
      TTroolean = record
        private type
          TThreeState = (TTrue = 1, TFalse = 0, TNil = -1);
    
        var
          fThreeState: TThreeState;
        public
          function AsString: string;
          class operator Implicit(Value: boolean): TTroolean;
          class operator Implicit(Value: TTroolean): boolean;
          class operator Implicit(Value: TThreeState): TTroolean;
          class operator Implicit(Value: TTroolean): TThreeState;
          class operator LogicalAnd(Left, Right: TTroolean): TTroolean;
          class operator LogicalOr(Left, Right: TTroolean): TTroolean;
          class operator LogicalNot(Value: TTroolean): TTroolean;
      end;
    
    implementation
    
    { TRoolean }
    
    class operator TTroolean.Implicit(Value: boolean): TTroolean;
    begin
      if Value then
        result.fThreeState := TTrue
      else
        result.fThreeState := TFalse;
    end;
    
    class operator TTroolean.Implicit(Value: TTroolean): boolean;
    begin
      if not(Value.fThreeState = TNil) then
        result := (Value.fThreeState = TTrue)
      else
        result := false;
    end;
    
    class operator TTroolean.Implicit(Value: TThreeState): TTroolean;
    begin
      result.fThreeState := Value;
    end;
    
    class operator TTroolean.Implicit(Value: TTroolean): TThreeState;
    begin
      result := Value.fThreeState;
    end;
    
    class operator TTroolean.LogicalAnd(Left, Right: TTroolean): TTroolean;
    begin
      if (Left.fThreeState = TNil) or (Right.fThreeState = TNil) then
        result.fThreeState := TNil
      else if ((Left.fThreeState = TTrue) and (Right.fThreeState = TTrue)) then
        result.fThreeState := TTrue
      else
        result.fThreeState := TFalse;
    end;
    
    class operator TTroolean.LogicalNot(Value: TTroolean): TTroolean;
    begin
      begin
        case value.fThreeState of
        TNil: result.fThreeState:= TNil;
        TTrue: result.fThreeState:= TFalse;
        TFalse: result.fThreeState:= TTrue
        end;
      end;
    
    end;
    
    class operator TTroolean.LogicalOr(Left, Right: TTroolean): TTroolean;
    begin
      if (Left.fThreeState = TNil) or (Right.fThreeState = TNil) then
        result.fThreeState := TNil
      else if ((Left.fThreeState = TTrue) or (Right.fThreeState = TTrue)) then
        result.fThreeState := TTrue
      else
        result.fThreeState := TFalse;
    end;
    
    function TTroolean.AsString: string;
    begin
      case ord(fThreeState) of
        1:
          result := 'TTrue';
        0:
          result := 'TFalse';
        -1:
          result := 'TNil';
      end;
    end;
    
    end. 
    

    И пример использования

    program ThreeStateLogicTest;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils,
      UnitTroolean in 'UnitTroolean.pas';
    
    var
      ABoolean: boolean;
      ATroolean, Anothertroolean, AThirdTroolean: TTroolean;
    
    begin
    
      try
        { TODO -oUser -cConsole Main : Insert code here }
    
        write('Boolean:', BoolToStr(ABoolean, true), #10#13);
        write(#10#13);
    
        ATroolean := TFalse;
        ABoolean := true;
        ATroolean := ABoolean;
    
        write('Boolean:', BoolToStr(ABoolean, true), #10#13);
        write('Troolean:', ATroolean.AsString, #10#13);
        write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
        write(#10#13);
    
        ATroolean := TTrue;
        ABoolean := false;
        ATroolean := ABoolean;
    
        write('Boolean:', BoolToStr(ABoolean, true), #10#13);
        write('Troolean:', ATroolean.AsString, #10#13);
        write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
        write(#10#13);
    
        ABoolean := false;
        ATroolean := TTrue;
        ABoolean := ATroolean;
    
        write('Boolean:', BoolToStr(ABoolean, true), #10#13);
        write('Troolean:', ATroolean.AsString, #10#13);
        write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
        write(#10#13);
    
        ABoolean := true;
        ATroolean := TFalse;
        ABoolean := ATroolean;
    
        write('Boolean:', BoolToStr(ABoolean, true), #10#13);
        write('Troolean:', ATroolean.AsString, #10#13);
        write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
        write(#10#13);
    
        ABoolean := false;
        ATroolean := Tnil;
        ABoolean := ATroolean;
    
        write('Boolean:', BoolToStr(ABoolean, true), #10#13);
        write('Troolean:', ATroolean.AsString, #10#13);
        write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
        write(#10#13);
    
        ABoolean := true;
        ATroolean := Tnil;
        ABoolean := ATroolean;
    
        write('Boolean:', BoolToStr(ABoolean, true), #10#13);
        write('Troolean:', ATroolean.AsString, #10#13);
        write('Troolean as Boolean:', BoolToStr(ATroolean, true), #10#13);
        write(#10#13);
    
        ATroolean := TTrue;
        Anothertroolean := false;
    
        AThirdTroolean := ATroolean and Anothertroolean;
        write('And:', AThirdTroolean.AsString, #10#13);
    
        AThirdTroolean := ATroolean or Anothertroolean;
        write('Or:', AThirdTroolean.AsString, #10#13);
    
        ATroolean := TNil;
        Anothertroolean:= not ATroolean;
        write('Not TNil:', Anothertroolean.AsString, #10#13);
    
        ATroolean := TTrue;
        Anothertroolean:= not ATroolean;
        write('Not Ttrue:', Anothertroolean.AsString, #10#13);
    
        ATroolean := Tfalse;
        Anothertroolean:= not ATroolean;
        write('Not Tfalse:', Anothertroolean.AsString, #10#13);
    
    
    
        readln;
    
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    
    end.
    
    22.09.2011
  • Я даже не знал, что в Delphi есть перегрузка операторов. 23.09.2011

  • 4

    Или... вы можете использовать Integer и привести его к логическому значению по мере необходимости. Используйте 0 для false, 1 для true и -1 для "nil". В любом случае, вы нарезаете его, вы не можете использовать логическую переменную, чтобы делать то, что хотите, вам понадобится другой тип или манипулирование параметрами, как предложил lynxnake.

    РЕДАКТИРОВАТЬ: Вариантом этого, который очень неэффективен, было бы использование Variant. С вариантом вы можете передавать значения Null (в некотором роде похожие на nil, но все же значение), а также Unassigned (также похожие на nil, но представляющие «нет значения»).

    22.09.2011

    5

    Самый простой способ — использовать перечисление (он же нумерованный тип).

    Это уже показано в ответе Грега Хьюгилла, но неправильно, вы не должны использовать предопределенные false и true в качестве идентификаторов перечисления¹. И внутри ответа HMcG — но внутри типа оболочки (более сложный пример). Я предлагаю написать что-то вроде: type TTrilean = (triFalse, triTrue, triNull);.

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


    Кроме того, вы можете написать функции-оболочки в соответствии с ответом Сергея Хейлыка:

    procedure DoSomething(Foo: Integer; Bar: TTrilean); overload;
    begin
        … //main code
    end;
    
    procedure DoSomething(Foo: Integer; Bar: Boolean); overload;
    const
        Trileans: array[Boolean] of TTrilean = (triFalse, triTrue);
    begin
        DoSomething(Foo, Trileans[Bar]);
    end;
    
    procedure DoSomething(Foo: Integer); overload;
    begin
        DoSomething(Foo, triNull);
    end;
    

    Вы даже можете сделать первую приватной, а две последние — общедоступной, если хотите.


    Примечания:
    1. Я думаю (не уверен), что формально вы можете написать type TMyType = (False, True, Unspecified);, так как False и True не являются зарезервированными словами. Но это нарушит ваш доступ к исходным False и True типа Boolean (после этого вам нужно будет ссылаться на них как System.False и System.True). И это несовместимо со сторонними компиляторами (например, FPC этого не допустит).

    27.01.2017
    Новые материалы

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

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..