「または」論理的に定義する方法


36

最近、論理的な「OR」演算子をプログラムで定義する必要がある問題に遭遇しましたが、演算子自体は使用していません。

私が思いついたのはこれです:

OR(arg1, arg2)
  if arg1 = True and arg2 = True
     return True

  else if arg1 = True and arg2 = False
     return True

  else if arg1 = False and arg2 = True
     return True

  else:
     return False

この論理は正しいですか、それとも何か見落としていましたか?


10
@gnat:公平を期すために、真理値表には入力の各組み合わせの出力がリストされ、Wikipediaの記事には関数の説明が記載されています。OPが本当に求めているのは、演算子自体を使用せずに論理的にORをプログラムで定義する方法だと思います。
Blrfl 14

6
@ user3687688使用が許可されているプリミティブを明確にしてください。
fredoverflow 14

4
この質問により、マイクロ最適化の集団的痙攣が始まりました;)
ロブ14

8
三項演算子return arg1 ? arg1 : arg2;
マシュー14

4
or演算子を再定義する必要がある理由を知る必要があります。
カイルストランド14

回答:


102

それは正しいと思いますが、このようなものに凝縮することはできませんか?

or(arg1, arg2)
    if arg1 == true
        return true
    if arg2 == true
        return true

    return false

あなたは比較をしているので、実際に組み合わせを確認する必要はないと思います。それらの1つがtrueを返すことがtrueであるかどうかだけが重要です。それ以外の場合は、falseを返します。

冗長性の少ない短いバージョンを探している場合、これも機能します。

or(arg1, arg2)
    if arg1
        return arg1
    return arg2

6
4行目の「else」を削除することもできます(そのまま残しますif arg2 == true)。
ドーソントス14

1
@DawsonTothスピンする方法はたくさんありますが、実際に冗長にするか凝縮するかによって異なります。私は他の場合には満足していますが、これは擬似コードの質問のように聞こえるので、明確にするためにおそらくこのようにしておきます。でも本当に!
エリオットブラックバーン14

@BlueHat else ifを使用することはわずかに矛盾しているようですが、最後にelseを使用することはできません。
SBoss 14

1
@Mehrdadありがとう!私はそれがもう少し冗長だと感じ、解決策を少し明確に説明するという理由だけで、古い答えを含めました。しかし、ソリューションははるかに小さく、同じ仕事をします。
エリオットブラックバーン

1
さらに良い(悪い):or(a, b): a ? a : b
サラ

149

以下は、比較とブールリテラルを使用しない、または使用しない、および使用しないソリューションです。

or(arg1, arg2)
  if arg1
    return arg1
  else
    return arg2

それはおそらくそれよりもはるかに基本的なものにはなりません;)


32
+1は私のものよりわずかに短い答えです。ただし、優雅さのためだけに「else」も削除したいと思うでしょう。
エリオットブラックバーン14

10
@BlueHatしかし、その後、2つのリターンは異なるインデントになります;)
fredoverflow 14

5
私は、毎回誰かが反対何かを比較EUR取得したいと思いtrueかをfalse
JensG

1
@JensGさて、ビル・ゲイツの収入はどこから来ていると思いますか?
Kroltan 14

1
||簡単なJavaScript 演算子(動的に型指定された言語で実装されている場合)。
サイ14

108

1行のコード:

return not (not arg1 and not arg2)

分岐もORもありません。

Cベースの言語では、次のようになります。

return !(!arg1 && !arg2);

これは単にDe Morganの法則の適用です:(A || B) == !(!A && !B)


6
(私の意見では)if/elseコンストラクトはORを使用するのと同じで、名前が異なるだけなので、このアプローチが最善のソリューションだと思います。
ニック14

2
@Nick using ifは等価と同等です。通常、マシンコードでifは、算術として実装され、その後にジャンプしてゼロと比較されます。


1
私はこのアプローチが好きです。なぜなら、IFF andショートサーキットをショートさせて、オペレーター間の一貫性を提供するからです。
カイルストランド14

1
@スノーマンそれは本当です。私はそれがif (a) return true; else if (b) return true;多かれ少なかれ道徳的に同等であるように思われたif (a OR b) return true;が、その見解は論争に対して開かれているかもしれない。
ニック14

13

としか持っていない場合はandnotDeMorganの法則を使って振り向くことができますand

if not (arg1 = False and arg2 = False)
  return True
else
  return False

...または(さらに簡単に)

if arg1 = False and arg2 = False
  return false
else
  return true

...

そして、とにかく機械命令としてほとんど常に利用できるものを最適化することに固執するようになったので、要約すると次のようになります。

return not(not arg1 and not arg2)

return arg1 ? true : arg2

などなどなど

ほとんどの言語は条件付きANDを提供するため、「AND」演算子は分岐を意味します。

...

持っているものがすべてある場合nandウィキペディアを参照):

return nand(nand(arg1、arg1)、nand(arg2、arg2))


7
簡素化:return not (not arg1 and not arg2)

@雪だるまは、あなたが本当にそれを答えにするべきです。あなたは(現在)分岐を行っていない唯一の人です。
ロートンフォーグル14

4
NANDソリューションを追加しようとしていましたが、あなたはそれに私を打ち負かしました。すべてをNANDの観点から実装する必要があります。
アンディ14

2
@Andy:実際には、すべてをNORで定義する必要があります。;-)
Pieter Geerkens 14

1
純粋なnandソリューションで良い仕事。
AAT 14

13

関数(ECMAScript)

必要なのは、関数定義と関数呼び出しだけです。分岐、条件、演算子、組み込み関数は必要ありません。ECMAScriptを使用した実装を示します。

まずは、という2つの関数を定義してみましょうtruefalse。自由に定義できますが、完全に任意ですが、後で見るようにいくつかの利点がある非常に特別な方法で定義します。

const tru = (thn, _  ) => thn,
      fls = (_  , els) => els;

truは、2番目の引数を単に無視し、最初の引数を返す2つのパラメーターを持つ関数です。flsまた、最初の引数を単に無視して2番目の引数を返す2つのパラメーターを持つ関数です。

なぜこのようにエンコードtruしたのflsですか?さて、このように、2つの関数はtrueand の2つの概念を表すだけfalseでなく、「選択」の概念も表す、つまりif/ then/ else式でもあります。if条件を評価し、thenブロックとelseブロックを引数として渡します。条件がに評価されるtruと、thenブロック評価される場合flselseブロックを返します。以下に例を示します。

tru(23, 42);
// => 23

これは戻ります 23、:

fls(23, 42);
// => 42

42期待どおりに戻ります。

しかし、しわがあります:

tru(console.log("then branch"), console.log("else branch"));
// then branch
// else branch

これは両方を印刷します then branchをしelse branchます!どうして?

まあ、それは返す最初の引数の戻り値を、それが評価される ECMAScriptのは厳密で、常に関数を呼び出す前に、関数のすべての引数を評価するので、両方の引数を。IOW:最初の引数であるを評価します。これはconsole.log("then branch")単に返され、コンソールに出力するというundefined副作用があります。then branchそれが二番目の引数を評価し、またその戻りundefined副作用としてコンソールへとプリント。次に、最初のを返しますundefined

このエンコーディングが発明されたλ計算では、それは問題ではありません。λ計算は純粋です。つまり、副作用がありません。したがって、2番目の引数も評価されることに気付かないでしょう。さらに、λ計算は遅延(または、少なくとも通常の順序で評価されることが多い)、つまり、必要のない引数を実際には評価しません。したがって、IOW:λ計算では、2番目の引数は評価されず、評価されても気付かないでしょう。

ただし、ECMAScriptはstrictです。つまり、常にすべての引数を評価します。まあ、実際には、常にではありません:たとえば、if/ then/ は、条件がある場合にのみブランチを評価し、条件がある場合にのみブランチを評価しますelsethentrueelsefalseます。そして、この振る舞いをiff。ありがたいことに、ECMAScriptは怠zyではありませんが、他のほとんどすべての言語と同じように、コードの評価を遅らせる方法があります:関数でラップし、その関数を呼び出さないとコードは決して実行されません。

したがって、両方のブロックを関数でラップし、最後に返される関数を呼び出します。

tru(() => console.log("then branch"), () => console.log("else branch"))();
// then branch

プリントthen branch

fls(() => console.log("then branch"), () => console.log("else branch"))();
// else branch

プリント else branchます。

従来のif/ then/をelse次のように実装できます。

const iff = (cnd, thn, els) => cnd(thn, els);

iff(tru, 23, 42);
// => 23

iff(fls, 23, 42);
// => 42

繰り返しますが、関数を呼び出すときに追加の関数のラッピングが必要であり、上記と同じ理由iffiff、追加の関数はの定義で括弧を呼び出します。

const iff = (cnd, thn, els) => cnd(thn, els)();

iff(tru, () => console.log("then branch"), () => console.log("else branch"));
// then branch

iff(fls, () => console.log("then branch"), () => console.log("else branch"));
// else branch

これらの2つの定義ができたので、実装できますor。最初に、真理値表を見てください。or第1オペランドが真理値の場合、式の結果は第1オペランドと同じです。それ以外の場合、式の結果は第2オペランドの結果です。つまり、第1オペランドがの場合、第1オペランドtrueを返します。それ以外の場合、第2オペランドを返します。

const orr = (a, b) => iff(a, () => a, () => b);

動作することを確認しましょう:

orr(tru,tru);
// => tru(thn, _) {}

orr(tru,fls);
// => tru(thn, _) {}

orr(fls,tru);
// => tru(thn, _) {}

orr(fls,fls);
// => fls(_, els) {}

すばらしいです!しかし、その定義は少しいように見えます。覚えておいてください、truそしてfls、すでに自分で条件付きのすべてのように行動するので、実際には必要ありませんiffので、すべてのすべてで、その関数のラッピングの:

const orr = (a, b) => a(a, b);

そこにあなたはそれを持っています:(orそして他のブール演算子)ほんの数行の関数定義と関数呼び出しだけで定義されています:

const tru = (thn, _  ) => thn,
      fls = (_  , els) => els,
      orr = (a  , b  ) => a(a, b),
      nnd = (a  , b  ) => a(b, a),
      ntt = a          => a(fls, tru),
      xor = (a  , b  ) => a(ntt(b), b),
      iff = (cnd, thn, els) => cnd(thn, els)();

返すECMAScriptのには関数や演算子がない:残念ながら、この実装はかなり無用であるtrufls、それらはすべて返却trueまたはfalse私達は私達の機能でそれらを使用することはできませんので、。しかし、私たちにできることはまだたくさんあります。たとえば、これは片方向リンクリストの実装です。

const cons = (hd, tl) => which => which(hd, tl),
      car  = l => l(tru),
      cdr  = l => l(fls);

オブジェクト(Scala)

奇妙なことに気づいたかもしれません:truそしてfls、二重の役割を果たし、データ値trueとの両方として機能しますfalseが、同時に条件式としても機能します。それらはデータ振る舞いであり、1つにまとめられています…ええと…「もの」…または(あえて言う)オブジェクト

確かに、truそしてflsオブジェクトです。また、Smalltalk、Self、Newspeak、またはその他のオブジェクト指向言語を使用したことがある場合は、それらがまったく同じ方法でブール値を実装していることに気付くでしょう。このような実装をScalaで実演します。

sealed abstract trait Buul {
  def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): T
  def &&&(other:Buul): Buul
  def |||(other:Buul): Buul
  def ntt: Buul
}

case object Tru extends Buul {
  override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): U = thn
  override def &&&(other:Buul) = other
  override def |||(other:Buul): this.type = this
  override def ntt = Fls
}

case object Fls extends Buul {
  override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): V = els
  override def &&&(other:Buul): this.type = this
  override def |||(other:Buul) = other
  override def ntt = Tru
}

object BuulExtension {
  import scala.language.implicitConversions
  implicit def boolean2Buul(b:Boolean) = if (b) Tru else Fls
}

import BuulExtension._

(2 < 3) { println("2 is less than 3") } { println("2 is greater than 3") }
// 2 is less than 3

BTWは、条件付きポリモーフィズムリファクタリングとの置換が常に機能する理由です。先ほど示したように、ポリモーフィックメッセージディスパッチは単に実装するだけで条件を置き換えることができるため、プログラム内のすべての条件を常に多態的なメッセージディスパッチに置き換えることができます。Smalltalk、Self、Newspeakなどの言語は、これらの言語には条件さえないため、その存在を証明しています。(また、ループ、BTW、または仮想メソッド呼び出しとも呼ばれるポリモーフィックメッセージディスパッチを除き、実際にはあらゆる種類の言語組み込み制御構造はありません。)


パターンマッチング(Haskell)

また、orパターンマッチング、またはHaskellの部分関数定義のようなものを使用して定義することもできます。

True ||| _ = True
_    ||| b = b

もちろん、パターンマッチングは条件付き実行の一種ですが、オブジェクト指向のメッセージディスパッチも同様です。


2
どの程度False ||| False = False_ ||| _ = True代わりに?:)
fredoverflow 14

3
@FredOverflow:正しいオペランドを常に評価する必要があります。通常、ブール演算子は、正しい引数、つまり「ショートサーキット」が厳密でないことが期待されています。
ヨルグWミットタグ14

ああ、もちろん。より深い理由がなければならないと知っていた:)
fredoverflow 14

最初の部分は、Eric Lippertの継続パッシングスタイルに関する素晴らしいシリーズをすぐに思い出しました。純粋に偶然ですが、まだ楽しいです:)
Voo 14

1
@JörgWMittagFredOverflowの定義は適切に短絡しています。True ||| undefinedghciで試してみてください!
ダニエルワグナー14

3

以下に、ORまたはそれを定義する最も伝統的な方法を使用した論理演算子を定義する別の方法を示します。真理値表を使用します。

もちろん、これはJavascriptやPerlなどの高水準言語で行うのは非常に簡単ですが、この手法が高水準言語機能に依存しないことを示すためにCでこの例を書いています。

#include <stdio.h>

int main (void) {
    // Define truth table for OR:
    int OR[2][2] = {
        {0,   // false, false
         1},  // false, true
        {1,   // true, false
         1}   // true, true
    }

    // Let's test the definition
    printf("false || false = %d\n",OR[1==2]['b'=='a']);
    printf("true || false = %d\n",OR[10==10]['b'=='a']);

    // Usage:
    if (OR[ 1==2 ][ 3==4 ]) {
        printf("at least one is true\n");
    }
    else {
        printf("both are false\n");
    }
}

AND、NOR、NAND、NOT、XORでも同じことができます。コードは、構文のように見えるほどきれいなので、次のようなことができます。

if (OR[ a ][ AND[ b ][ c ] ]) { /* ... */ }

これはある数学的な意味での「純粋な」アプローチだと思います。結局、OR演算子は関数であり、真理値表は実際には関係と集合としてのその関数の本質です。もちろん、これはあまりにも面白いOOの方法で書くことができますBinaryOperator or = new TruthTableBasedBinaryOperator(new TruthTable(false, true, true, true));
から来た

3

論理演算子を整数算術式として表現する別の方法(可能な場合)。この方法は、多くの述語のより大きな表現のために多くの分岐を避けることができます

Trueを1にするFalseを0にする

両方の合計が1より大きい場合、trueまたはfalseが返されます。

boolean isOR(boolean arg1, boolean arg2){

   int L = arg1 ? 1 : 0;
   int R = arg2 ? 1 : 0;

   return (L+R) > 0;

}

6
booleanExpression ? true : falseは、と等しいbooleanExpression
キーン14

私はあなたの方法論が好きですが、単純な間違いは、両方の引数の合計がゼロよりも大きくなければならないということです。
確かに14

1
return (arga+argb)>0
確かに14

1
私はあなたのテキストを修正するだけでした。あなたのコードは完璧ですが、1行で記述できます:return (((arg1 ? 1 : 0)+(arg2 ? 1 : 0)) > 0); :)
14

1
@SenthuSivasambuの使用に異議はありませんarg1 ? 1 : 0;。これらは、ブール値を数値に変換するための信頼できる式です。簡単にリファクタリングできるのはreturnステートメントのみです。
キーン14

1

2つの形式:

OR(arg1, arg2)
  if arg1
     return True
  else:
     return arg2

または

OR(arg1, arg2)
  if arg1
     return arg1
  else:
     return arg2

コードと同様に、これまでの他の提案よりも少し小さいというゴルフのような利点があり、ブランチが1つ少なくなります。私たちが非常に頻繁に使用されるプリミティブの作成を検討している場合、ブランチの数を減らすことはそれほど愚かではありません。

Javascriptの定義は||これに似ており、その緩いタイピングと組み合わせることで、式false || "abc"に値が"abc"あり、値があることを意味し42 || "abc"ます42

既に他の論理演算子を持っている場合nand(not(arg1), not(arg2))は、そのようなものには分岐がまったくないという利点があるかもしれませんが。


あなたが認めたように)前の答えを繰り返すことのポイントは何ですか?
ブヨ

@gnatそれは私がその答えを見れば気にならないほど十分に近いですが、それらのいずれにも見つからない何かがまだあるので、私はそれを残しています。
ジョンハンナ14

@gnat、実際に「いくつかの説明とコンテキストを提供する長い答えを探しています」と考えています。私は今、この答えに満足しています。
ジョンハンナ14

1

ifコンストラクトを使用するすべてのプログラムされたソリューションに加えて、3つのNANDゲートを組み合わせてORゲートを構築することができます。ウィキペディアでそれがどのように行われているかをご覧になりたい場合は、ここをクリックしてください

これから、式、

NOT [NOT(A AND A)AND NOT(B AND B)]

NOTとANDを使用すると、ORと同じ答えが返されます。NOTとANDの両方を使用することは、NANDを表すあいまいな方法にすぎないことに注意してください。


NOT(A AND A)== NOT(A)?
チャーリー14

はい、正確に。同じウィキペディアの記事で、NOTゲートをNANDゲートに削減する方法を確認できます。ANDゲートでも同じです。ORゲート用に提示した式は編集しないことにしました。
ウォルターミッティ14

1

すべての良い答えはすでに与えられています。しかし、私はそれを止めさせません。

// This will break when the arguments are additive inverses.
// It is "cleverness" like this that's behind all the most amazing program errors.
or(arg1, arg2)
    return arg1 + arg2
    // Or if you need explicit conversions:
    // return (bool)((short)arg1 + (short)arg2)

代わりに:

// Since `0 > -1`, negative numbers will cause weirdness.
or(arg1, arg2)
    return max(arg1, arg2)

このようなアプローチを実際に誰も使用しないことを願っています。彼らはここで、代替案の認識を促進するためだけにいます。

更新:

負の数は上記のアプローチの両方を破ることができるため、別のひどい提案があります:

or(arg1, arg2)
    return !(!arg1 * !arg2)

これは単にDeMorganの法則を使用*&&、when trueおよびand falseがそれぞれ1およびのように扱われることに類似する事実を悪用し0ます。(待って、これコードゴルフではないと言っているのですか?)

これはまともな答えです:

or(arg1, arg2)
    return arg1 ? arg1 : arg2

しかし、それはすでに与えられた他の答えと本質的に同じです。


3
これらのアプローチには根本的な欠陥があります。+ -1 1を考慮しarg1+arg2、-1と0 max(arg1,arg2)等、
ふわふわ

@fluffyこのアプローチではブール引数を想定しているため、たまたまほとんどの種類のガベージ入力で正しく動作します。問題を引き起こすごみがまだ残っていることを指摘してください。この種のことは、実際に実際の問題領域を可能な限り直接モデル化する(そして、私たち自身の賢さに夢中になることを避ける)ことを模索するべき理由です。
キーン14

純粋な1ビットのブール値を実行している場合、1 + 1 = 0であるため、加算はまだ機能しません。:)
fluffy

@fluffyそこが明示的な変換の出番です。それらが必要かどうかは、実装の詳細に依存します(これが馬鹿げた考えである理由です)。
キーン14

0

定義する1つの方法orは、ルックアップテーブルを使用することです。これを明示的にすることができます:

bool Or( bool a, bool b } {
  bool retval[] = {b,true}; // or {b,a};
  return retval[a];
}

返り値が何であるかに応じて持つべき値で配列を作成しますa。次に、ルックアップを行います。C ++のような言語でboolは、truebeing 1およびfalsebeing を使用して、配列インデックスとして使用できる値に昇格します0

次に、これを他の論理演算に拡張できます。

bool And( bool a, bool b } {
  bool retval[] = {false,b}; // or {a,b};
  return retval[a];
}
bool Xor( bool a, bool b } {
  bool retval[] = {b,!b};
  return retval[a];
}

これらすべての欠点は、プレフィックス表記が必要になることです。

namespace operators {
  namespace details {
    template<class T> struct is_operator {};
    template<class Lhs, Op> struct half_expression { Lhs&& lhs; };
    template<class Lhs, class Op>
    half_expression< Lhs, Op > operator*( Lhs&&lhs, is_operator<Op> ) {
      return {std::forward<Lhs>(lhs)};
    }
    template<class Lhs, class Op, class Rhs>
    auto operator*( half_expression<Lhs, Op>&& lhs, Rhs&& rhs ) {
    return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
    }
  }
  using details::is_operator;
}

struct or_tag {};
static const operators::is_operator<or_tag> OR;

bool invoke( bool a, or_tag, bool b ) {
  bool retval[] = {b,true};
  return retval[a];
}

そして今、あなたは入力することができます true *OR* false、それは動作します。

上記の手法には、引数依存のルックアップをサポートする言語とテンプレートが必要です。おそらくジェネリックとADLを使用した言語でそれを行うことができます。

余談ですが*OR*、セットを操作するために上記を拡張できます。以下invokeと同じ名前空間に無料の関数を作成しますor_tag

template<class...Ts>
std::set<Ts...> invoke( std::set<Ts...> lhs, or_tag, std::set<Ts...> const& rhs ) {
  lhs.insert( rhs.begin(), rhs.end() );
  return lhs;
}

そして今set *OR* set、2つの和集合を返します。


0

これは私に特徴的な機能を覚えています:

or(a, b)
    return a + b - a*b

これは、ブール値を(1、0)として処理できる言語にのみ適用されます。booleanはクラスであるため、SmalltalkまたはPythonには適用されません。smalltalkではさらに進んでいきます(これは一種の擬似コードで記述されます)。

False::or(a)
    return a

True::or(a)
    return self

また、次の2つの方法があります。

False::and(a)
    return self

True::and(a)
    return a

そのため、OPステートメントでは「ロジック」は完全に有効ですが、冗長ではあります。注意してください、それは悪くありません。たとえば、一種のマトリックスに基づいた数学演算子のように機能する関数が必要な場合に最適です。他のものは、実際のキューブを実装します(Quine-McCluskeyステートメントのような):

or = array[2][2] {
    {0, 1},
    {1, 1}
}

そして、あなたは評価するでしょう[または] [b]

そのため、ここでのすべてのロジックは有効です(ただし、言語内OR演算子xDDDDDDDDを使用して投稿されたものは除きます)。

しかし、私のお気に入りはDeMorganの法則です: !(!a && !b)


0

Swift標準ライブラリを見て、ショートカットORおよびショートカットAND演算の実装を確認してください。これらの演算は、必要でない場合や許可されていない場合に2番目のオペランドを評価しません


-2

ロジックは完全に正しいですが、単純化できます:

or(arg1, arg2)
  if arg1 = True
     return True
  else if arg2 = True
     return True
  else
     return False

そして、おそらくあなたの言語はOR演算子を持っているので、質問の精神に反しない限り-

or(arg1, arg2)
  if arg1 = True or arg2 = True
     return True
  else
     return False

if arg1 = True or arg2 = True { return true } else { return false }いっそのこと、return arg1 = True or arg2 = Trueif condition then true else false冗長です。
ドーバル14

4
アスカーは、具体的には、それらの要件は、「オペレータ自体を使用せずに」あったことを指摘
GNAT

2
ええと、私はそのようなことを何も言わなかった。それは私が意図したことの一種でしたが、編集されるまで質問はそう言わなかったので、彼女はそのように答えました。
logicNoob 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.