左から右への言語構文の利点


18

私はChannel9 でハーブサッターとインタビューを見ていましたが、彼はビデオの最後で、左から右への言語構文が将来のC ++標準のウィッシュリストの一番上にあると述べました(彼はそのようにC ++を変更することを認めていますがまったく別の獣になります)。

の他に:

  • 人間がより理解しやすく、肉眼で明確

    //C syntax
    
    /*pointer to function taking a pointer to function(which takes 2 integers as 
    
    arguments and returns an int), and an int as arguments and returning an int*/
    
    int (*fp)(int (*ff)(int x, int y), int b)
    
    //Go analogous syntax which is left to write
    
    f func(func(int,int) int, int) int
  • 解析しやすくなります(ビデオで説明したように、ツールのサポートが向上します-コードリファクタリングなど)

プログラミング言語の「左から右」の構文には、他にどのような利点がありますか。私はPascalとGoがその種の構文を使用していることしか知っていません(そして、私はこのブログ投稿から理解しているように、Goも完全には行きません)構文の?


1
Haskellの用途は、左から右に:f :: (Int -> Int -> Int) -> Int -> Int
カロリー・ホーバス

1
同様のActionScript: function strlen(s:String):int {...}。また、型付きラムダ計算(したがって、Haskell)。
-outis

2
誰もが終了票を説明してください:)?それを閉じる理由はわかりませんが、「間違った」質問をしているのかもしれません。

1
私は締め切りに投票しませんでしたが、@ Devjoshのコメントは適切です。プログラマにとってより適切であり、誰かが移行することを願っています。...-
Nim

3
@Frank:関数ポインターの場合、実際の型が分割されているため、構文が厄介であることを忘れないでください!それは...低打撃だ
マシューM.

回答:


12

基本的な利点は、解析がよりシンプルでユニークであることです。行が解析された後、コンパイラは正確な型が何であるかを知るので、それ以降、型がどのように定義されたかは関係ないことに注意してください。

配列型の引数または関数ポインタ型を返す関数は、現在読みにくいです:

// common way of obtaining the static size in elements of an array:
template <typename T, int N>
char (&array_size_impl( T (&)[N] ))[N];
// alternative parser:
template <typename T, int N>               // this would probably be changed also
array_simple_impl function( T [N] & ) char [N] &;

そして、誤解の可能性が少なくなります(最もベキシング解析として):

// Current C++
type x( another_type() );      // create an instance x of type passing a
                               // default constructed another_type temporary?
                               // or declare a function x that returns type and takes as argument
                               // a function that has no arguments and returns another_type
// How the compiler reads it:
x function( function() another_type ) type;

// What many people mean:
x type { another_type{} };

C ++ 0xでの均一な初期化への同様のアプローチの使用(つまり{}、初期化の識別)。左から右へのアプローチでは、定義する内容がはるかに明確になることに注意してください。多くの人々(確かに私)は、過去に(2回以上)この解析エラーに噛まれたことがあり、左から右の構文の場合はそうではありません。


表現はどうですか?この「左から右への構文」は、演算子の優先順位と評価の順序にどのように影響しますか?

1
@celavek:インタビューに戻ると、彼は言語の構文全体を変更するのではなく、宣言定義のみを変更することに気付くでしょう。もちろん、それは他の式にも浸透する可能性があり、上記の例の最後の行が左から右に適切であるかどうかはあまりわかりません(C#で、一時ファイルの作成方法を考えてください... 2つの意味を与えることによって解決されるnewため、オペレータstruct又はclassC ++のようなCに適用されない、++値/基準タイプには区別がない。
デビッドロドリゲス- dribeas

5

ここでどうやって

関数ポイントを宣言するためのC構文は、使用法を反映することを目的としていました。次のような通常の関数宣言を考えます<math.h>

double round(double number);

ポイント変数を使用するには、タイプセーフティを使用してそれを割り当てることができます。

fp = round;

あなたはそれを宣言する必要があります fpこのようにポイント変数を次のように。

double (*fp)(double number);

そのため、あなたがしなければならないことは、関数の使い方を見て、その関数の名前をポインタ参照に置き換えて、にするroundことだけ*fpです。ただし、余分なカレンのセットが必要です。これは、少し厄介になると言う人もいます。

おそらく、これは関数のシグネチャさえも持たない元のCで以前は簡単でしたが、そこに戻らないようにしましょう。

特に厄介なのは、引数として受け取る関数、関数へのポインターを返す関数、またはその両方を宣言する方法を見つけることです。

機能がある場合:

void myhandler(int signo);

次のようにしてシグナル関数(3)に渡すことができます:

signal(SIGHUP, myhandler);

または、古いハンドラーを保持する場合は、

old_handler = signal(SIGHUP, new_handler);

とても簡単です。非常に簡単なこと-また、きれいでも簡単でもないことは、宣言を正しくすることです。

signal(int signo, ???)

さて、あなたはあなたの関数宣言に戻り、ポイントリファレンスの名前を交換するだけです:

signal(int sendsig, void (*hisfunc)(int gotsig));

宣言していないためgotsig、省略すると読みやすくなる場合があります。

signal(int sendsig, void (*hisfunc)(int));

またはそうでないかもしれません。:(

ただし、以下のようにsignal(3)も古いハンドラーを返すため、それでは十分ではありません。

old_handler = signal(SIGHUP, new_handler);

したがって、これらすべてを宣言する方法を理解する必要があります。

void (*old_handler)(int gotsig);

割り当てる変数には十分です。gotsigここで実際に宣言しているのではないことに注意してくださいold_handler。これで本当に十分です:

void (*old_handler)(int);

これにより、signal(3)の正しい定義が得られます。

void (*signal(int signo, void (*handler)(int)))(int);

レスキューへのTypedef

この時までに、誰もがそれが混乱であることに同意すると思う。抽象化に名前を付ける方が良い場合があります。しばしば、本当に。これにより、次のtypedefことが理解しやすくなります。

typedef void (*sig_t) (int);

独自のハンドラー変数は

sig_t old_handler, new_handler;

そしてsignal(3)の宣言はちょうど

sig_t signal(int signo, sig_t handler);

これは突然理解できます。*を削除すると、混乱を招く括弧も削除されます(そして、かっこは常に物事を理解しやすくします-ハァッ!)。あなたの使用法は同じです:

old_handler = signal(SIGHUP, new_handler);

しかし、今、あなたはの宣言を理解する機会を持っているold_handlernew_handlerとさえsignalあなたが最初にそれらまたはそれらを記述する必要が発生したとき。

結論

非常に少数のCプログラマーが、参考資料を参照することなくこれらの事柄について正しい宣言を独力で考案できることがわかった。

カーネルとデバイスドライバーの仕事をしている人々へのインタビューの質問でこの質問があったことがあるからです。:)確かに、ホワイトボードでクラッシュして燃えたため、そのようにして多くの候補者を失いました。しかし、この分野での以前の経験はあるが、実際に仕事をすることができなかったと主張する人々を雇うことも避けました。

しかし、この広範囲にわたる困難のため、これを使用するために平均値の3シグマ以上に座っているトリプルアルファオタクプログラマーである必要がなくなったすべての宣言を実行する方法を持つことは、おそらく賢明なだけでなく、実際に合理的です。快適に。


1
作られた、従うのは難しい... Cでそれを正しくするのが時々難しいという点を示すように、努力のために+1
celavek

4

左から右のビットに焦点を合わせたとき、あなたはその点を見逃したと思います。

CとC ++の問題は、それらが持つ恐ろしい文法であり、読むこと(人間)と解析すること(ツール)の両方が困難です。

より一貫した(または通常の)文法を使用すると、両方で簡単になります。そして、より簡単な構文解析はより簡単なツールを意味します:ほとんどの現在のツールは、車輪を再発明しようとしたため、Eclipseの最新のプラグインでさえもC ++が正しく機能しません...そして失敗しました。

それで、あなたはおそらく読書と構文解析に集中するときにそれを釘付けにした...そしてそれは大したことだ:)


これは、Eclipseが上記の宣言のようなものをまだ解析できないことに大きな驚きです。のような本物のCパーサーを使用しないのはなぜgccですか?
-tchrist

@tchrist:Eclipseで2つの問題を検出しましたが、どちらもマクロにリンクしているようです。おそらく、AST生成よりもプリプロセッサの問題です。
マチューM.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.