ブールパーサーを作成する(続き)


8

著者がいなくなって質問が終了したため、この課題の継続。


ブールパーサーを作成する必要があります。


ブール式は、まだ聞いたことがない場合に備えて、2つの入力と1つの出力を備えています。

つまり、ブール演算には4つの「ゲート」があります。

  • OR(で表される|)(2項演算子、引数の間)
  • AND(で表される&)(2項演算子、引数間)
  • XOR(で表される^)(2項演算子、引数間)
  • NOT(で表される!)(単項演算子、右側の引数)

これらのゲートは、真(で表される1)または偽(で表される0)の入力に対して動作します。次のように真理値表を使用して、可能な入力(AおよびBこの場合)と出力(O)をリストできます。

XOR
A|B|O
-----
0|0|0
0|1|1
1|0|1
1|1|0

OR
A|B|O
-----
0|0|0
0|1|1
1|0|1
1|1|1

AND
A|B|O
-----
0|0|0
0|1|0
1|0|0
1|1|1

NOT
A|O
---
0|1
1|0

入力例はで1^((1|0&0)^!(1&!0&1))、次のように評価されます。

 1^((1|0&0)^!(1&!0&1))
=1^(( 1 &0)^!(1&!0&1))
=1^(   0   ^!(1&!0&1))
=1^(   0   ^!(1& 1&1))
=1^(   0   ^!(  1 &1))
=1^(   0   ^!    1   )
=1^(   0   ^    0    )
=1^0
=1

出力はになります1

細部

  • 例に見られるように、流行の順序はありません。最初に評価する必要がある括弧内の場合を除いて、すべて左から右に評価されます。
  • 入力にはのみが含まれます()!^&|01
  • 上記の8文字を置き換えるために任意の8バイト文字を選択できますが、それらには1対1のマッピングがあり、明記する必要があります。
  • 具体的には、入力から派生したeval文字列に対して関数を使用することはできません。具体的には、関数(または言語で同等のもの)とそれを呼び出す関数は、では使用できません。また、内部の文字列にを連結することはできません。inputevalinputeval

得点

これはです。バイト単位の最短のソリューションが優先されます。


チェダーには文字列からコールスタックを生成するためのビルトインがあり、それも許可されていませんか?
Downgoat

2
さらにテストケースを追加していただけませんか?
Conor O'Brien

@CᴏɴᴏʀO'Bʀɪᴇɴどんなテストケースを見たいですか?
Leaky Nun

@KennyLauいくつかの複雑なもの、idk
Conor O'Brien

テストケースが処理しなかった条件はありますか?私はそれはすでにほとんどすべてを処理したと思います。
Leaky Nun

回答:


6

JavaScript(ES6)116バイト

thx @ user81655を編集して3バイトを保存し、バグが見つかった

s=>[...s].map(x=>x<2?v=m[x]:x<6?m=[,,m[1]+m[0],0+v,v+1,v+(1^v)][x]:x>6?v=o.pop()[v]:m=o.push(m)&&a,o=[v=a=m='01'])|v

おそらく最善のアプローチではありませんが、evalやブール演算子はなく、真理値表のみです。

使用される文字:

  • !-> 2
  • &-> 3
  • | -> 4
  • ^-> 5
  • (-> 6
  • )-> 7

テスト

f=s=>[...s].map(x=>x<2?v=m[x]:x<6?m=[,,m[1]+m[0],0+v,v+1,v+(1^v)][x]:x>6?v=o.pop()[v]:m=o.push(m)&&a,o=[v=a=m='01'])|v

console.log=(...x)=>O.textContent+=x+'\n'

test=[ ["1^((1|0&0)^!(1&!0&1))",1] 
// be more careful, step by step
,["0&0",0],["0&1",0],["1&0",0],["1&1",1]
,["0|0",0],["0|1",1],["1|0",1],["1|1",1]
,["0^0",0],["0^1",1],["1^0",1],["1^1",0]
,["0&!0",0],["0&!1",0],["1&!0",1],["1&!1",0]
,["0|!0",1],["0|!1",0],["1|!0",1],["1|!1",1]
,["0^!0",1],["0^!1",0],["1^!0",0],["1^!1",1]
,["!0&0",0],["!0&1",1],["!1&0",0],["!1&1",0]
,["!0|0",1],["!0|1",1],["!1|0",0],["!1|1",1]
,["!0^0",1],["!0^1",0],["!1^0",0],["!1^1",1]
// nand, nor
,["!(0&0)",1],["!(0&1)",1],["!(1&0)",1],["!(1&1)",0]
,["!(0|0)",1],["!(0|1)",0],["!(1|0)",0],["!(1|1)",0]
     ]

test.forEach(([x,check]) => {
  // remap operators (each one on its line, just to be clear)
  var t = x.replace(/!/g,"2")
  t = t.replace(/&/g,"3")
  t = t.replace(/\|/g,"4")
  t = t.replace(/\^/g,"5")
  t = t.replace(/\(/g,"6")
  t = t.replace(/\)/g,"7")
  r = f(t)
  console.log((r==check?'OK':'KO')+' '+x +' '+r)
})
<pre id=O></pre>


1
もしかしてx>7
ニール、

r=f(x.replace(/./g,c=>"01!&|^()".indexOf(c)))
ニール、

@ニール、チェックします、ありがとう。もちろん> 6を
超える

@ニール私はテストケースを追加しています。演算子の結合性と可換性を考えると、NOTは常に機能するはずです
edc65

なぜ(たとえば)0|!0機能するのかを理解するのに少し時間がかかりましたが、今は賛成票を持っています。
Neil、

5

Retina、49バイト

+`(<1>|!0|1[ox]0|0[ox]1|1[ao]1)|<0>|!1|\d\w\d
$#1

どうしてこんなに短かったのかはわかりません。

キャラクターマッピング:

^ -> x
& -> a
| -> o
( -> <
) -> >

10、および!変更されません。

これは、すべてのtruthy式(単一置換することによって動作し1、括弧内の、!01&11^00|1と、等)1、及び(単一他のすべての0、括弧内の!11&01^10|0を有する、等)0

オンラインでお試しください!
自動キャラクターマッピングでオンラインでお試しください!


3

grep + shell utils、131バイト

rev|grep -cP '^(((0|R((?9)(x(?1)|a(?4))|(?2)([oxa](?4)|a(?1)|))L|(1|R(?1)L)!)(!!)*)[xo](?1)|(1|R(?1)L|(?2)!)([ao](?1)|[xo](?4)|))$'

次の文字の名前が変更されました。

( -> L
) -> R
| -> o
& -> a
^ -> x

私はgrepソリューションを書き始めましたが、それが左結合インフィックス演算子ではうまく機能しないことを発見しました。(演算子のチェーン)=(演算子のチェーン)(バイナリ演算)(単一オペランド)のようなパターンが必要でしたが、これには無限再帰が含まれる可能性があるため、grepはそれを実行しません。しかし、右連想演算子を解析できることに気づきました。これは!オペレーターに苦痛をもたらしましたが、それでも可能でした。そのため、ブール式を逆算するための正規表現を作成し、を介して入力を送信しましたrev。正規表現に一致する正規表現自体は116バイトです。

TODO:使用するすべての演算子グループを組み込みの文字クラスで区別できるように、入力に異なる文字を選択します。


どういう(?9)意味ですか?
Leaky Nun

これは、9番目のキャプチャグループを取得して再実行すること\9を意味します(これは、9番目のキャプチャグループが一致したものと一致することとは異なります)。したがって、たとえば(\d)\1、同じ数字を2回(\d)(\?1)照合し、任意の2桁を照合します。
Neil、

2

Python、210バイト

from operator import*;
def t(o):c=o.pop(0);return ord(c)-48if c in"01"else[p(o),o.pop(0)][0]if"("==c else 1-t(o)
def p(o):
 v=t(o)
 while o and")"!=o[0]:v=[xor,or_,and_]["^|&".index(o.pop(0))](v,t(o))
 return v

本当にひどい再帰降下ですが、これはハートビートで打たれると思います。


2

Mathematicaの、139の 129バイト

a=StringPartition;StringReplace[#,{"(0)0&00&11&00|00^01^1"~a~3|"!1"->"0","(1)1&10|11|01|10^11^0"~a~3|"!0"->"1"},1]&~FixedPoint~#&

単純な文字列置換ソリューションは、期待していたよりもはるかに優れたスコアを示します。


2

JavaScript ES6、223バイト

x=>(Q=[],S=[],[...x].map(t=>{q=+t&&Q.push(t);if(t==")")while((a=S.pop())!="(")Q.push(a);else if(!q)S.push(t)}),k=[],p=_=>k.pop(),Q.concat(S.reverse()).map(e=>k.push(+e||e<"#"?1-p():e<"'"?p()&p():e<","?p()|p():p()^p())),p())

分路ヤードアルゴリズムを使用します。

x=>(Q=[],S=[],[...x].map(t=>{q=+t&&Q.push(t);if(t==")")while((a=S.pop())!="(")Q.push(a);else 
if(!q)S.push(t)}),k=[],p=_=>k.pop(),Q.concat(S.reverse()).map(e=>k.push(+e||e<"#"?1-p():e<"'"
?p()&p():e<","?p()|p():p()^p())),p())

用途+のためのOR、!否定のために、^XORのために、と&のためにと。0そして1、それぞれの値に使用されています。確かに、オペレーターに番号を付けることでゴルフをすることはできましたが、たとえJavaScriptの賞を受賞したわけではないので、少なくともある程度は読みやすく正確なものにしたいと思いました。


1

C、247

ゴルフ:

b(char*s){int i,j,k,l;if(*s==40){for(j=i=1;i+=s[++j]==41?-1:s[j]==40?1:0,i;);s[j++]=0;b(s+1);sprintf(s,"%s%s",s+1,s+j);}!s[1]?:(b(s+1),i=*s,j=1,k=s[1],i>47&i<50?:(s[1]=i==33?(j=0,k^1):(l=s[-1],i==38?k&l:i==94?k^l|'0':k|l),sprintf(s-j,"%s",s+1)));}

main()(式を第1引数としてとる)でゴルフをしていません。ゴルフバージョンにはデバッグprintfsがなく、charリテラル(40 == '(')の代わりに2桁のASCIIコードを使用します。マッピング()|^&!することで一部の文字を保存できた234567でしょう-これにより、多くの操作とテスト48をそれぞれから差し引いた後、より簡単になったでしょう。

char*z;                 // tracks recursion depth; not used in golfed version
b(char*s){
    int i,j,k,l;
    printf("%u> '%s'\n", s-z, s);
    if(*s=='('){        // handles parenthesis
        for(j=i=1;i+=s[++j]==')'?-1:s[j]=='('?1:0,i;);
        s[j++]=0;
        b(s+1);         // s+1 to s+j gets substituted for its evaluation
        sprintf(s,"%s%s",s+1,s+j);
    }
    !s[1]?:(            // if 1 char left, return
        b(s+1),         // evaluate rest of expression
        i=*s,
        j=1,
        k=s[1],
        printf("%u: '%c'\n", s-z, i),
        i>47&i<50?:(    // if 0 or 1, skip substitution
                        // otherwise, perform boolean operation
            s[1]=i=='!'?(j=0,k^1):(l=s[-1],i=='&'?k&l:i=='|'?k|l:k^l|'0'),
                        // and replace operation with result
            sprintf(s-j,"%s",s+1),printf("%u= '%s'\n", s-z, s-j)));
    printf("%u< '%s'\n", s-z, s);
}
int main(int argc, char **argv){
    char *s;    
    sscanf(argv[1],"%ms",&s);
    z=s;
    b(s);
    printf("%s => %s\n", argv[1], s);
}

の+1 for(j=i=1;i+=s[++j]==')'?-1:s[j]=='('?1:0,i;);
Leaky Nun

1

Java、459バイト

String p(String s){int x,y;while((y=s.indexOf("b"))>=0){x=s.lastIndexOf("a",y);s=s.replaceAll(s.subString(x,y+1),p(s.subString(x+1,y)));}String t,a="1",b="0";while(s.indexOf("!")>=0){s=s.replaceAll("!0",a);s=s.replaceAll("!1",b);}while(s.length()>1){t=s.subString(0,3);if(t.charAt(1)=='l')s=s.replaceFirst(t,t.equals("0l0")?b:a);else if(t.charAt(1)=='&')s=s.replaceFirst(t,t.equals("1&1")?a:b);else s=s.replaceFirst(t,t.charAt(0)==t.charAt(2)?b:a);}return s;}

AND です &

ORl(小文字のL)

XORis x(またはStringのメソッドでたまたま再生される他の任意のキャラクターString.replaceAll(...)

NOT です !

( です a

) です b

これはより読みやすいバージョンです:

String parseBoolean( String str ) {
    int start,end;
    //look for matching brackets ab
    while( (end = str.indexOf( "b" )) >= 0 ) {
        start = str.lastIndexOf( "a", end );
        str = str.replaceAll( str.subString( start, end + 1 ), parseBoolean( str.subString( start + 1, end ) ) );
    }
    String temp, one = "1", zero = "0";
    //handle all the !'s
    while( str.indexOf( "!" ) >= 0 ) {
        str = str.replaceAll( "!0", one );
        str = str.replaceAll( "!1", zero );
    }
    //handle the remaining operators from left to right
    while( str.length() > 1 ){
        temp = str.subString( 0, 3 );
        //check for OR
        if( temp.charAt( 1 ) == 'l' )
            str = str.replaceFirst( temp, temp.equals( "0l0" ) ? zero : one );
        //check for AND
        else if(t.charAt(1)=='&')
            str = str.replaceFirst( temp, temp.equals( "1&1" ) ? one : zero );
        //handle XOR
        else 
            str = str.replaceFirst( temp, temp.charAt( 0 ) == temp.charAt( 2 ) ? zero : one );
    }
    return str;
}

オンラインで試す


1
Javaゴルフでいつものように、私の好きなことは、可能な限りcharリテラルを対応する整数に置き換えることです。この場合、それは通常のindexOfとcharAtの比較の両方にあります。また、ANDの文字を「&」ではなく「n」に変更する場合、実行する必要がある操作を確認するときに、単一のifで<または>ステートメントを使用できます。
ブルー

1
あ、もう一つ。2番目のwhileループでreplaceAllの呼び出しを2倍にして、これらのブラケットを節約することもできます。
ブルー

@Blue私はいつもintにリテラルをcharするのを忘れています、ありがとう。!のreplaceAll呼び出しを2倍にすることの意味がよくわかりません。
Jack Ammo

s = s.replaceAll( "!0"、a).replaceAll( "!1"、b);
ブルー

1

Java、218

パターンマッチングを使用しますが、以前に失敗したJavaの試みの順序が狂った置換を回避します(鋭い目があります、@ Kenny Lau!)。

ゴルフ:

String e(String s){Matcher m=Pattern.compile("(<1>|1o[01]|0o1|1a1|1x0|0x1|n0)|(<0>|0o0|0a[01]|1a0|1x1|0x0|n1)").matcher(s);return m.find()?e(s.substring(0,m.start())+(m.group(1)==null?"0":"1")+s.substring(m.end())):s;}

Ungolfedは、引数からの入力を読み取り、マッピングを適用するoaxnため|&^!<>のために()

import java.util.regex.*;

public class B{
    String e(String s){
        System.out.println(s);
        Matcher m=Pattern
            .compile(
                "(<1>|1o[01]|0o1|1a1|1x0|0x1|n0)|"+
                "(<0>|0o0|0a[01]|1a0|1x1|0x0|n1)")
            .matcher(s);
        return m.find()?e(s.substring(0,m.start())+(m.group(1)==null?"0":"1")+s.substring(m.end())):s;
    }

    public static String map(String s, String ... replacements) {
        for (String r: replacements) {
            s = s.replace(r.substring(0,1), r.substring(1));
        }
        return s;
    }

    public static void main(String ... args){
        for (String s: args) System.out.println(new B().e(
            map(s,"(<",")>","|o","&a","!n","^x")
        ));
    }
}

Java m.group(i)はどのグループが一致したかを教えてくれます。1番目のグループは真の置換用で、2番目のグループは偽の置換用です。これは、置換が実行されなくなるまで、厳密な左から右の順序で繰り返されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.