43

プログラミングをしていると、特定の制御構造が非常に役立つことがありますが、プログラミング言語では直接使用できません。私の最も一般的な欲求は「分割中」のようなものだと思います(実際にこれを何と呼ぶか​​わかりません):

{
    foo();
} split_while( condition ) {
    bar();
}

このコードのセマンティクスは、foo()常に実行され、条件がチェックされるというものです。trueの場合、bar()が実行され、最初のブロックに戻ります(したがって、foo()再度実行するなど)。redditユーザーzxqdmsによるコメントのおかげで、Donald E. Knuthが彼の論文「Structuredprogrammingwithstatements go to(279ページを参照)でこの構造について書いていることを知りました。

計算を整理するのに役立つ代替制御構造は何だと思いますか?

ここでの私の目標は、チャンク化と推論を改善するために、コードの構造化について自分自身と他の人に新しい考え方を与えることです。

:Lispマクロ、継続、モナド、コンビネータ、クォークなどを使用して、考えられるすべての制御構造を一般化する方法については質問していません。コードを記述するのにどの専門分野が役立つのか尋ねています。jneifgoto

4

28 に答える 28

20

かなり一般的なのは無限ループです。私はそれをこのように書きたいです:

forever {
  // ...
}
于 2010-11-28T02:12:11.320 に答える
20

場合によっては、インデックスを使用して foreach ループが必要になることがあります。次のように記述できます。

foreach (index i) (var item in list) {
  // ...
}

(私はこの構文が特に好きではありませんが、アイデアはわかります)

于 2010-11-28T03:14:19.980 に答える
19

ほとんどの言語には、一般的なケースをカバーする組み込み関数がありますが、「フェンスポスト」ループは常に雑用です: 各反復で何かを実行し、反復間で何か他のことを実行するループです。たとえば、区切り文字を使用して文字列を結合します。

string result = "";
for (int i = 0; i < items.Count; i++) {
    result += items[i];
    if (i < items.Count - 1) result += ", "; // This is gross.
    // What if I can't access items by index?
    // I have off-by-one errors *every* time I do this.
}

折り畳みがこのケースをカバーできることは知っていますが、何か命令的なことが必要な場合もあります。あなたができるならそれはクールだろう:

string result = "";
foreach (var item in items) {
    result += item;
} between {
    result += ", ";
}
于 2010-11-28T08:59:53.083 に答える
18

else でループ:

while (condition) {
  // ...
}
else {
  // the else runs if the loop didn't run
}
于 2010-11-28T03:57:53.593 に答える
13
{
    foo();
} split_while( condition ) {
    bar();
}

通常の を使用すると、非常に簡単に実現できますwhile

while (true) {
    foo();
    if (!condition) break;
    bar();
}

に対する不合理な嫌悪感を克服した今、私はかなり頻繁にそれを行いbreakます。

于 2010-11-27T21:12:32.523 に答える
13

Haskell を見ると、さまざまな制御構造に特殊な構文がありますが、制御フローは型によって捉えられることがよくあります。このようなコントロール タイプの最も一般的な種類は、モナド、アロー、およびアプリケーション ファンクターです。したがって、特殊なタイプの制御フローが必要な場合は、通常、ある種の高階関数であり、自分で作成するか、かなり大きい Haskells パッケージ データベース (Hackage) で見つけることができます。

このような関数は通常、エラー処理への並列実行用のモジュールを見つけることができる Control 名前空間にあります。手続き型言語で通常見られる制御構造の多くは、Control.Monad に対応する関数があり、その中にはループと if ステートメントがあります。if-else は haskell のキーワード式であり、 if がなければ式では意味がありませんが、モナドでは完全に意味があるため、 else のない if ステートメントは関数whenandによってキャプチャされますunless

別の一般的なケースは、より一般的なコンテキストでリスト操作を行うことです。関数型言語は が非常に好きでfold、特殊化されたバージョンはmapfilter. モナドがある場合、そのモナドの自然な拡張がありfoldます。これは と呼ばれfoldM、そのため、 や のように考えられる特別なバージョンの fold の拡張機能もありmapMますfilterM

于 2010-11-27T23:04:52.457 に答える
11

This is just a general idea and syntax:

if (cond)
   //do something
else (cond)
   //do something
also (cond)
   //do something
else
   //do something
end

ALSO condition is always evaluated. ELSE works as usual.

It works for case too. Probably it is a good way to eliminate break statement:

case (exp)
   also (const)
      //do something
   else (const)
      //do something
   also (const)
      //do something
   else
      //do something
end

can be read as:

switch (exp)
   case (const)
      //do something
   case (const)
      //do something
      break
   case (const)
      //do something
   default
      //do something
end

I don't know if this is useful or simple to read but it's an example.

于 2010-11-28T11:33:49.977 に答える
10

(lispスタイルの)マクロ、末尾呼び出し、および継続を使用すると、これらすべてが古風な趣のあるものになります。

マクロを使用すると、標準の制御フロー構造が特定のアプリケーションに十分でない場合、プログラマーは独自の(およびそれ以上の)記述を行うことができます。例として示した構成を実装するには、単純なマクロのみが必要です。

末尾呼び出しを使用すると、複雑な制御フローパターン(ステートマシンの実装など)を関数に組み込むことができます。

継続は強力な制御フロープリミティブです(try / catchはそれらの制限付きバージョンです)。末尾呼び出しやマクロと組み合わせると、複雑な制御フローパターン(バックトラッキング、解析など)が簡単になります。さらに、制御の反転を反転できるので、Webプログラミングで役立ちます。ユーザーに入力を求めたり、処理を行ったり、ユーザーにさらに入力を求めたりする関数を使用できます。

スキーム標準を言い換えると、言語にさらに多くの機能を積み上げるのではなく、他の機能が必要と思われる制限を取り除くように努める必要があります。

于 2010-11-27T21:38:46.027 に答える
8
if (cond)
   //do something
else (cond)
   //do something
else (cond)
   //do something
first
   //do something
then
   //do something
else (cond)
   //do something
else
   //do something
end

FIRSTおよびTHENブロックは、3つの条件のいずれかがtrueと評価された場合に実行されます。FIRSTブロックは条件付きブロックの前に実行され、THENは条件付きブロックの実行後に実行されます。

FIRSTおよびTHENステートメントに続くELSE条件付きまたは最終書き込みは、これらのブロックから独立しています。

それは次のように読むことができます:

if (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   //do something
else
   //do something
end


function first()
   //do something
return
function then()
   //do something
return

これらの関数は、読むための単なる形式です。彼らはスコープを作成しませんでした。これは、Basicからのgosub/returnのようなものです。

議論の問題としての有用性と読みやすさ。

于 2010-11-28T12:25:18.463 に答える
8

そうでない場合:

unless (condition) {
  // ...
}

そうではありませんが:

until (condition) {
  // ...
}
于 2010-11-28T03:31:09.690 に答える
8

「then」演算子を提案します。最初の反復では左側のオペランドを返し、他のすべての反復では右側のオペランドを返します。

var result = "";
foreach (var item in items) {
    result += "" then ", ";
    result += item;
}

最初の反復では、"" が結果に追加され、他のすべての反復では ", " が追加されるため、コンマで区切られた各項目を含む文字列が得られます。

于 2010-11-28T09:56:04.857 に答える
8

ラベル付きループは、主流の言語に欠けていることがあります。例えば、

int i, j;
for outer ( i = 0; i < M; ++i )
    for ( j = 0; j < N; ++j )
        if ( l1[ i ] == l2[ j ] )
           break outer;

はい、通常はこれを でシミュレートできますgotoが、同等の for ではcontinue、インクリメントをラベルの後にループ本体の最後に移動する必要があり、読みやすさが損なわれます。内側のループにフラグを設定し、外側のループの各反復でそれをチェックすることによってこれを行うこともできますが、常にぎこちなく見えます。

(おまけ: a をand とredo一緒に使用したい場合があります。インクリメントを評価せずにループの先頭に戻ります。)continuebreak

于 2010-11-28T07:07:51.777 に答える
6

どうですか

alternate {
    statement 1,
    statement 2,
    [statement 3,...]
}

連続する各パスで使用可能なステートメントを循環するため。

編集:簡単な例

table_row_color = alternate(RED, GREEN, BLUE);

player_color = alternate(color_list); // cycles through list items

alternate(
    led_on(),
    led_off()
);

編集 2 : 上記の 3 番目の例では、関数のように見えるため、構文が少しわかりにくいかもしれません。実際、両方ではなく、各パスで 1 つのステートメントのみが評価されます。より良い構文は次のようなものかもしれません

alternate {
    led_on();
}
then {
    led_off();
}

またはその趣旨の何か。ただし、必要に応じて呼び出された結果を使用できるという考えが気に入っています (色の例のように)。

于 2010-11-28T03:17:03.417 に答える
5

Dのスコープガードは、あまり見られない便利な制御構造です。

于 2010-11-28T09:43:31.160 に答える
5

本当に凝ったループ構造を持つCityScript ( CityDeskのスクリプト言語)について言及する必要があると思います。

ヘルプ ファイルから:

{$ forEach n var in (condition) sort-order $}
... text which appears for each item ....
{$ between $}
.. text which appears between each two items ....
{$ odd $}
.. text which appears for every other item, including the first ....
{$ even $}
.. text which appears for every other item, starting with the second ....
{$ else $}
.. text which appears if there are no items matching condition ....
{$ before $}
..text which appears before the loop, only if there are items matching condition
{$ after $}
..text which appears after the loop, only of there are items matching condition
{$ next $}
于 2010-12-03T13:10:27.710 に答える
4

ignoring- 特定のコード ブロックで発生する例外を無視する。

try {
  foo()
} catch {
  case ex: SomeException => /* ignore */
  case ex: SomeOtherException => /* ignore */
}

コントロール構造を使用すると、次のignoringように、より簡潔に、より読みやすく記述できます。

ignoring(classOf[SomeException], classOf[SomeOtherException]) {
  foo()
}

[ Scala はこれ (および他の多くの例外処理制御構造) を標準ライブラリのutil.controlパッケージで提供します。]

于 2010-11-28T03:57:14.780 に答える
4

代わりになるもの

bool found = false;
for (int i = 0; i < N; i++) {
  if (hasProperty(A[i])) {
    found = true;
    DoSomething(A[i]);
    break;
  }
}
if (!found) {
  ...
}

お気に入り

for (int i = 0; i < N; i++) {
  if (hasProperty(A[i])) {
    DoSomething(A[i]);
    break;
  }
} ifnotinterrupted {
  ...
}

ループ本体の最後の (通常の) 実行の後に何かを実行するためだけにフラグを導入するよりも良い方法があるに違いないと常に感じています。をチェックできます!(i < N)i、ループ後に範囲外です。

于 2010-12-03T00:29:17.127 に答える
4

出力をグループ化するためのキーワードが欲しいです。これの代わりに:

        int lastValue = 0;

        foreach (var val in dataSource)
        {
            if (lastValue != val.CustomerID)
            {                    
                WriteFooter(lastValue);
                WriteHeader(val);
                lastValue = val.CustomerID;
            }
            WriteRow(val);
        }
        if (lastValue != 0)
        {
            WriteFooter(lastValue);
        }

このようなものはどうですか:

        foreach(var val in dataSource)
        groupon(val.CustomerID)
        {            
            startgroup
            {
                WriteHeader(val);
            }
            endgroup
            {
                WriteFooter(val)
            }
        }
        each
        {
            WriteRow(val);
        }

まともなプラットフォーム、コントロール、および/またはレポートの書式設定があれば、このコードを書く必要はありません。しかし、驚くべきことに、自分がこれを頻繁に行っていることに気づきます。最も厄介な部分は、最後の繰り返しの後のフッターです。コードを複製せずに実際の例でこれを行うのは困難です。

于 2010-12-03T00:32:10.630 に答える
4

また、特定のモナドに応じて、多くの制御構造がモナドのコンテキストで新しい意味を持つことに注意してください。Haskell の mapM、filterM、whileM、sequence などを見てください。

于 2010-11-27T21:59:19.380 に答える
3
for int i := 0 [down]to UpperBound() [step 2]

すべての C 派生言語で欠落しています。

投票したり、コメントを書いたりする前に考慮してください:
これは と冗長ではありませんfor (int i = 0; i <= UpperBound(); i++)

  1. UpperBound()一度だけ評価される

  2. ケースUpperBound() == MAX_INTは無限ループを生成しません

于 2010-12-03T00:08:14.007 に答える
3

これはちょっとした冗談ですが、次のようにして必要な動作を得ることができます。

#include <iostream>
#include <cstdlib>

int main (int argc, char *argv[])
{
  int N = std::strtol(argv[1], 0, 10); // Danger!
  int state = 0;
  switch (state%2) // Similar to Duff's device.
  {
    do {
      case 1: std::cout << (2*state) << " B" << std::endl;
      case 0: std::cout << (2*state+1) << " A" << std::endl; ++state;
    } while (state <= N);
      default: break;
  }

  return 0;
}

ps フォーマットするのは少し難しかったので、私は満足していません。しかし、emacs はさらに悪いことをします。vimを試してみたい人はいますか?

于 2010-11-28T00:47:25.370 に答える
3

これはおそらくカウントされませんが、Python では do ループがないことに腹を立てました。

Anto は、この回答に賛成票が入らないようにします。goto がない期間、使用している言語にイライラしてしまいます。

于 2010-11-28T06:58:58.503 に答える
3

Python のジェネレーターは、非関数型言語を主に扱ってきた場合、まったく新しいものです。より一般的には、継続、コルーチン、遅延リストです。

于 2010-11-28T04:28:48.197 に答える
2

これは、@Paul Keisterによる応答に似ています。

(つぶやく) 何年も前に、私が取り組んでいたアプリケーションには、いわゆるコントロール ブレーク処理のバリエーションがたくさんありました。すべてのロジックは、並べ替えられたデータの行を、ヘッダーとフッターを持つグループとサブグループに分割します。アプリケーションは LISP で作成されているため、WITH-CONTROL-BREAKS というマクロで一般的なイディオムを取得しました。その構文を常に人気のある波線の形に置き換えるとしたら、次のようになります。

withControlBreaks (x, y, z : readSortedRecords()) {
  first (x) :     { emitHeader(x); subcount = 0; }
  first (x, y) :  { emitSubheader(x, y); zTotal = 0; }
  all (x, y, z) : { emitDetail(x, y, z); ztotal += z; }
  last (x, y) :   { emitSubfooter(x, y, zTotal); ++subCount; }
  last (x) :      { emitFooter(x, subcount); }
}

この現代では、SQL、XQuery、LINQ などが広く普及しているため、この必要性は以前ほど生じていないようです。しかし、ときどき、その制御構造が手元にあればいいのにと思います。

于 2010-12-12T00:58:22.653 に答える
2
foo();

while(condition)
{
   bar();
   foo();
}
于 2010-11-28T03:06:49.880 に答える
1

多くの言語で使用できない制御構造の 1 つは、ケースイン型構造です。switch 型構造と同様に、可能なオプションのきれいにフォーマットされたリストを持つことができますが、(入力に一致する最初のものではなく) true である最初のものに一致します。そのようなLISP(それを持っています):

(cond
   ((evenp a) a)        ;if a is even return a
   ((> a 7) (/ a 2))    ;else if a is bigger than 7 return a/2
   ((< a 5) (- a 1))    ;else if a is smaller than 5 return a-1
   (t 17))              ;else return 17

または、より C に似た形式を好む人のために

cond 
    (a % 2 == 0): 
        a;     break;
    (a > 7):
        a / 2; break;
    (a < 5):
        a - 1; break;
    default:
        17;    break;

これは基本的に、スイッチよりも構造を正確に表現するif/elseif/elseif/elseものであり、そのロジックをクリーンで読みやすい方法で表現するのに非常に役立ちます。

于 2011-11-14T21:56:06.137 に答える
1

PL/I スタイルの "for" ループ範囲はどうですか? VB に相当するものは次のとおりです。

' カウント 1、2、... 49、50、23、999、998、...、991、990
  I = 1 ~ 50、23、999 ~ 990 の場合 Step -1

私が見ることができる最も一般的な使用法は、インデックスのリストに対してループを実行し、次にもう 1 つスローすることです。ところで、For-Each の使用法も便利です。

' Bar1、Bar2、Bar3 は IEnum(Wazoo) です。ボズはワズー
  Bar1、Bar2、Enumerable.One(Boz)、Bar3 の Wazoo としての各 Foo について

これにより、Bar1 のすべてのアイテム、Bar2、Boz、および Bar3 のすべてのアイテムでループが実行されます。おそらく、Linq はそれほど問題なくこれを可能にしますが、組み込み言語のサポートはもう少し効率的かもしれません。

于 2010-12-02T18:59:24.390 に答える
0

リストを移動するウィンドウ(1ではなくn個の要素)で反復するのはどうですか?これは接線方向に関連している@munificentの 答えだと思います。

何かのようなもの

#python
#sum of adjacent elements
for x,y in pairs(list):
    print x + y

def pairs(l):              
    i=0                    
    while i < len(l)-1:    
        yield (l[i],l[i+1])
        i+=1               

特定の種類のものに役立ちます。誤解しないでください。これは関数として実装するのは簡単ですが、仕事のためのより具体的で説明的なツールがある場合、多くの人が引き出してループforしようとすると思います。while

于 2012-06-06T17:06:45.360 に答える