2016-04-24 6 views
3

以下に相当するLINQはありますか?LINQを使用した状態での関数合成からのブレーク

static T Compose<T>(T source, 
    IEnumerable<Func<T, T>> funcs, 
    Func<T, bool> cond) 
{ 
    foreach (var func in funcs) 
    { 
     source = func(source); 

     if (cond(source)) break; 
    } 

    return source; 
} 

// This ends the composition as soon as a null is returned 
var result = Compose(x, funcs, x => x == null); 

は基本的に、私は、互いの上に機能を構成し、現在の値が特定の条件に一致する場合に破ることを望みます。

result = f(g(h(x))) // if condition is met somewhere within this chain, 
        // it will immediately break and return 

編集:私は

funcs.Aggregate(seed, (x, f) => cond(x) ? x : f(x)); 

の線に沿って何かを考えが、それは混乱に見えるとも条件があるとき、コレクション全体にわたり列挙ではなく、壊して終わります会った

+0

関数funcs.Aggregate((cur、next)=>(i => {var val = cur(i); return cond(val)?next(val):val; })) ' – reptile

答えて

0

これを行う1つの方法は、条件が満たされるまでAggregateです。だから、私は、拡張メソッドを定義します。

public static TAccumulate AggregateUntil<TSource, TAccumulate>(
    this IEnumerable<TSource> source, 
    TAccumulate seed, 
    Func<TAccumulate, TSource, TAccumulate> func, 
    Func<TAccumulate, bool> predicate) 
{ 
    if (source == null) 
     throw new ArgumentNullException(nameof(source)); 

    if (func == null) 
     throw new ArgumentNullException(nameof(func)); 

    if (predicate == null) 
     throw new ArgumentNullException(nameof(func)); 

    var accumulate = seed; 
    foreach (var item in source) 
    { 
     accumulate = func(accumulate, item); 
     if (predicate(accumulate)) break; 
    } 
    return accumulate; 
} 

我々は今のようにComposeUntilを書くことがあります。

public static T ComposeUntil<T>(
    this IEnumerable<Func<T, T>> source, 
    T seed, 
    Func<T, bool> predicate) 
{ 
    return source.AggregateUntil(seed, (x, f) => f(x), predicate); 
} 

そして、次のようにそれを使用する:条件が満たされていない後に計算を停止するには

var funcs = new List<Func<int, int>> 
{ 
    x => x + 1, 
    x => x + 2, 
    x => x + 3, 
    x => x + 4 
}; 

var result = funcs.ComposeUntil(0, x => x >= 6); 
// result == 6 
-1
var funcs = new Func<int, int>[] {i => i + 1, i => i * 2, i => i - 3}; 
Func<int, bool> cond = i => i < 20; 
var combined = 
    funcs.Aggregate(
     (cur, next) => 
      i => 
      { 
       var val = cur(i); 
       return cond(val) ? next(val) : val; 
      }); 
Console.WriteLine(combined(10)); // 22 

、連鎖呼び出しに置き換え、そのループので、スタックのサイズによって制限のfuncsの長さ。

+0

条件の使用はwroですng。これをOPコードで実行すると、 '22'ではなく' 11'が返されます。 – Enigmativity

-3

はい、例えば.Take(1)
を使用します。[編集]
繰り返しになりますが、あなたの問題の鍵は、 "LINQを使用して条件に機能組成物からブレークは" .FirstOrDefaultを使用している

var strList = new List<string> {"a","b","c","d"}; 
var str = strList.Where(x => x.Equals("c")).Take(1); 

または.Take(1)。

@Enigmativityのリクエスト/回答ごとのコード全体がここにあります。しかし、@Enigmativityがないように私のコードは、任意のサードパーティ製のパッケージを必要としませんのでご注意:これはうまく動作

static T Compose<T>(T source, 
     IEnumerable<Func<T, T>> funcs, 
     Func<T, bool> cond) 
    { 
     var func = funcs.Select(x => 
     { 
      var ret = x; 
      source = x(source); 
      return ret; 
     }) 
     .FirstOrDefault(y => cond(source)); 

     Console.WriteLine("Here is the Func that the condition succeeded on(null if none):"); 
     Console.WriteLine(func); 

     return source; 
    } 

void Main() 
{ 
    var funcs = new Func<int, int>[] 
    { 
     a => a + 1, 
     a => a + 2, 
     a => a + 3, 
    }; 

    // Set this "cond = b => b == ?" value to want your 
    // func will succeed on. Since we are continuing to add 
    // to the source, the three Func's above will succeed on 
    // any of the following values: 1, 3, 6 and immediately exit 
    // ending any further enumeration via the ".FirstOrDefault" 

    Func<int, bool> cond = b => b == 3; 

    Console.WriteLine("Here are the list of Funcs:"); 
    Console.WriteLine(funcs); 

    var result = Compose(0, funcs, cond); 

    Console.WriteLine(); 
    Console.WriteLine("Below is the actual result of the source after being "); 
    Console.WriteLine("acted on by the Funcs. If no Func matched the condition,"); 
    Console.WriteLine("all Funcs acted on the source so the source will return 6."); 
    Console.WriteLine("That means;"); 
    Console.WriteLine("If the first Func matched (aka cond = b => b == 1), the source will return 1."); 
    Console.WriteLine("If the second Func matched (aka cond = b => b == 3), the source will return 3."); 
    Console.WriteLine("If the third Func matched (aka cond = b => b == 6), the source will return 6. Same "); 
    Console.WriteLine("as matching no Func because all Funcs were applied. However, see above that 'null'"); 
    Console.WriteLine("was displayed for the 'Here is the Func that the condition succeeded on' when "); 
    Console.WriteLine("you set the condition value to an int other than 1, 3, or 6."); 
    Console.WriteLine(); 
    Console.WriteLine("Actual result of the source after being acted on by the Funcs retuned:"); 
    Console.WriteLine(result); 
} 
+0

なぜ投票が遅れたのですか?この例では、現在の値が特定の条件と一致する場合、linqを使用してforeachループから抜け出す方法を説明します。それは本質的に彼の質問です。 –

+0

'var str = strList.FirstOrDefault(x => x.Equals(" c "));' 現在の値が特定の条件に一致したときにブレークアウトして、コレクション全体を列挙しません。 [https://msdn.microsoft.com/en-us/library/bb503062(v=vs.110).aspx](https://msdn.microsoft.com/en-us/library/bb503062(v) = vs.110).aspx) および[https://msdn.microsoft.com/en-us/library/bb340482(v=vs.110).aspx](https://msdn.microsoft.com/en) -us/library/bb340482(v = vs.110).aspx) –

+1

あなたのコードがOPの 'Compose'メソッドの本体をどのように置き換えることができるかを示すことができますか? – Enigmativity

1

var funcs = new Func<string, string>[] 
{ 
    t => t + "1", 
    t => t + "2", 
    t => t + "3", 
}; 

Func<string, bool> cond = t => t.Length == 3; 

Func<string, string> func = 
    t => funcs.Aggregate(t, (a, f) => !cond(a) ? f(a) : a); 

Console.WriteLine(func("A")); 

それは長さ3"A12"文字列を生成します。

リアクティブエクステンションチームのインタラクティブエクステンション(NuGet "Ix-Main")から取得した新しいオペレータ.Scan(...)をコレクションを反復しないようにする必要があります。

.Scan(...)は、.Aggregate(...)のように機能しますが、各ステップで値を生成します。

Func<string, string> func = 
    t => funcs 
     .Scan(t, (a, f) => f(a)) 
     .SkipWhile(x => !cond(x)) 
     .FirstOrDefault(); 

これは、上記と同じ結果を生成しますが、コレクション全体を反復しません。あなたは本当に頻繁なヘルパー関数をしたい場合は、私はその後、

(1)

static T Compose<T>(T source, 
    IEnumerable<Func<T, T>> funcs, 
    Func<T, bool> cond) 
{ 
    return funcs.Aggregate(source, (a, f) => !cond(a) ? f(a) : a); 
} 

(2)

static T Compose<T>(T source, 
    IEnumerable<Func<T, T>> funcs, 
    Func<T, bool> cond) 
{ 
    return 
     funcs 
      .Scan(source, (a, f) => f(a)) 
      .SkipWhile(t => !cond(t)) 
      .FirstOrDefault(); 
} 
+0

条件が満たされた後でも 'funcs'コレクション全体を列挙します。 – MarcinJuraszek

+0

@MarcinJuraszek - その作業中です。 – Enigmativity

+0

@MarcinJuraszek - 完了。 – Enigmativity

-1

:ここ

Compose方法に挿入両方のオプションですthis questionを拡張メソッドとして作成し、どこでも使用することをお勧めします(ヒントと同様ですが、限られた集計。

string value = "Tes"; 

var funcs = new Func<string, string>[] 
{ 
    x => x + "t", 
    x => x + "s", 
    x => x + "s" 
}; 

funcs.TakeWhile(f => 
{ 
    if (value == "Tests") // your condition 
    { 
     return false; 
    } 

    value = f(value); // reassignment of value 
    return true; 
}).ToList(); 

Console.WriteLine(value); 

編集:より簡潔バージョン: - 私は

string value = "Tes"; 

var funcs = new Func<string, string>[] 
{ 
    x => x + "t", 
    x => x + "s", 
    x => x + "s" 
}; 

funcs.TakeWhile(f => value != "Tests" && (value = f(value)) is string).ToList(); 

Console.WriteLine(value); 
+0

なぜこれが投票されたのですか?あなたは説明したいですか? –

0

@Mateen Ulhaqあなただけめったに回、それを必要とする場合)

しかし、その後、私のようなものとなるだろう彼らが灰色になって以来私の更新された答え(および以下のコメント)を見ることができるかどうかわからないので、私は私の答えを再投稿しています。このコードを実行して、変数のint値を変更するときに何をしているのかを確認するには:
1)LinqPad4を開きます。
2)言語を "C#プログラム"に設定します。
3)ここに表示されているコードをすべて削除してくださいvoid main()..など
4)ここで自分のコードをコピーしてLinqPadに貼り付けます。
5)F5キーを押します。

static T Compose<T>(T source, 
     IEnumerable<Func<T, T>> funcs, 
     Func<T, bool> cond) 
    { 
     var func = funcs.Select(x => 
     { 
      var ret = x; 
      source = x(source); 
      return ret; 
     }) 
     .FirstOrDefault(y => cond(source)); 

     Console.WriteLine("Here is the Func that the condition succeeded on(null if none):"); 
     Console.WriteLine(func); 

     return source; 
    } 

void Main() 
{ 
    var funcs = new Func<int, int>[] 
    { 
     a => a + 1, 
     a => a + 2, 
     a => a + 3, 
    }; 

    // Set this "cond = b => b == ?" value to want your 
    // func will succeed on. Since we are continuing to add 
    // to the source, the three Func's above will succeed on 
    // any of the following values: 1, 3, 6 and immediately exit 
    // ending any further enumeration via the ".FirstOrDefault" 

    Func<int, bool> cond = b => b == 3; 

    Console.WriteLine("Here are the list of Funcs:"); 
    Console.WriteLine(funcs); 

    var result = Compose(0, funcs, cond); 

    Console.WriteLine(); 
    Console.WriteLine("Below is the actual result of the source after being "); 
    Console.WriteLine("acted on by the Funcs. If no Func matched the condition,"); 
    Console.WriteLine("all Funcs acted on the source so the source will return 6."); 
    Console.WriteLine("That means;"); 
    Console.WriteLine("If the first Func matched (aka cond = b => b == 1), the source will return 1."); 
    Console.WriteLine("If the second Func matched (aka cond = b => b == 3), the source will return 3."); 
    Console.WriteLine("If the third Func matched (aka cond = b => b == 6), the source will return 6. Same "); 
    Console.WriteLine("as matching no Func because all Funcs were applied. However, see above that 'null'"); 
    Console.WriteLine("was displayed for the 'Here is the Func that the condition succeeded on' when "); 
    Console.WriteLine("you set the condition value to an int other than 1, 3, or 6."); 
    Console.WriteLine(); 
    Console.WriteLine("Actual result of the source after being acted on by the Funcs retuned:"); 
    Console.WriteLine(result); 
} 
+0

ありがとうございました。残念ながら、私はお互いの上に関数を構成したいと思っていました。 '((4 + 1)+2)+3)= 10'を生成する' 'funcs'に対して、' Compose(4、funcs、x => false) 'は生成する必要があります。 –

+0

こんにちは@Mateen Ulhaq。はい、そうです。上記のコードでは、 'var result = Compose(0、funcs、cond);'を 'var result = Compose(4、funcs、cond); 'に変更し、LinqPadでこのコードを実行すると10を返します。私は何かを理解していない、正しい答えとしてこれを選択してください。 –

+0

@Mateen Ulhaq。私はここで私がここで使った 'FirstOrDefault(')構文が好きですが、 '.Take(1)'はよりクリーンです。 (1); '。これは私の最初の答えであったので、これは私の最初の答えでした。あなたはそれが灰色で表示されているかどうかわからなかったので、私はそれを再投稿しました。私の評判が修正されるように答えを受け入れました。 –

関連する問題