Przemyślenia na foreach z Enumerable.Range vs traditional for loop

W C # 3.0 podoba mi się ten styl:

// Write the numbers 1 thru 7
foreach (int index in Enumerable.Range( 1, 7 ))
{
    Console.WriteLine(index);
}

Przez tradycyjną pętlę for:

// Write the numbers 1 thru 7
for (int index = 1; index <= 7; index++)
{
    Console.WriteLine( index );
}

Zakładając, że 'n' jest małe, więc wydajność nie jest problemem, czy ktoś sprzeciwia się nowemu stylowi zamiast tradycyjnemu stylowi?

Author: Vadim Ovchinnikov, 2009-05-27

16 answers

Uważam, że format "minimum-to-maximum" tego ostatniego jest dużo jaśniejszy niż styl "minimum-count" Range w tym celu. Poza tym, nie sądzę, że jest to dobra praktyka, aby dokonać takiej zmiany od normy, która nie jest szybsza, nie krótsza, nie bardziej znana i oczywiście nie jaśniejsza.

To powiedziawszy, nie jestem przeciwny idei w ogóle. Gdybyś podszedł do mnie ze składnią, która wyglądała jak foreach (int x from 1 to 8), prawdopodobnie zgodziłbym się, że byłoby to ulepszenie w stosunku do pętli for. Jednak {[3] } jest dość niezgrabny.
 41
Author: mquander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-27 14:00:53

To tylko dla Zabawy. (Sam użyłbym standardowego formatu pętli " for (int i = 1; i <= 10; i++)".)

foreach (int i in 1.To(10))
{
    Console.WriteLine(i);    // 1,2,3,4,5,6,7,8,9,10
}

// ...

public static IEnumerable<int> To(this int from, int to)
{
    if (from < to)
    {
        while (from <= to)
        {
            yield return from++;
        }
    }
    else
    {
        while (from >= to)
        {
            yield return from--;
        }
    }
}

Możesz również dodać metodę rozszerzenia Step:

foreach (int i in 5.To(-9).Step(2))
{
    Console.WriteLine(i);    // 5,3,1,-1,-3,-5,-7,-9
}

// ...

public static IEnumerable<T> Step<T>(this IEnumerable<T> source, int step)
{
    if (step == 0)
    {
        throw new ArgumentOutOfRangeException("step", "Param cannot be zero.");
    }

    return source.Where((x, i) => (i % step) == 0);
}
 36
Author: LukeH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-27 16:33:47

W C # 6.0 z wykorzystaniem

using static System.Linq.Enumerable;

Można uprościć do

foreach (var index in Range(1, 7))
{
    Console.WriteLine(index);
}
 10
Author: Tsayper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:41:59

Możesz to zrobić w C# (podając To i Do jako metody rozszerzenia odpowiednio na int i IEnumerable<T>):

1.To(7).Do(Console.WriteLine);
SmallTalk forever!
 9
Author: THX-1138,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-17 20:39:09

Wydaje się, że dość długie podejście do problemu, który już został rozwiązany. Za Enumerable.Range jest cała maszyna stanowa, która nie jest tak naprawdę potrzebna.

Tradycyjny format jest fundamentalny dla rozwoju i znany wszystkim. Nie widzę żadnej przewagi w Twoim nowym stylu.

 8
Author: spender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:53:38

Myślę, że foreach + Enumerable.Zakres jest mniej podatny na błędy (masz mniej kontroli i mniej sposobów na zrobienie tego źle, jak zmniejszenie indeksu wewnątrz ciała, aby pętla nigdy się nie skończyła, itp.)

Problem z czytelnością dotyczy semantyki funkcji Range, która może zmieniać się z jednego języka na inny (np. jeśli podano tylko jeden parametr, będzie zaczynał się od 0 LUB 1, czy jest końcem włączonym lub wyłączonym lub jest drugim parametrem licznikiem zamiast wartością końcową).

O wydajność, myślę, że kompilator powinien być wystarczająco inteligentny, aby zoptymalizować obie pętle, aby wykonywały z podobną prędkością, nawet przy dużych zakresach (przypuszczam, że ten zakres nie tworzy kolekcji, ale oczywiście iterator).

 6
Author: fortran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-27 14:18:38

Podoba mi się ten pomysł. Jest bardzo podobny do Pythona. Oto moja wersja w kilku linijkach:

static class Extensions
{
    public static IEnumerable<int> To(this int from, int to, int step = 1) {
        if (step == 0)
            throw new ArgumentOutOfRangeException("step", "step cannot be zero");
        // stop if next `step` reaches or oversteps `to`, in either +/- direction
        while (!(step > 0 ^ from < to) && from != to) {
            yield return from;
            from += step;
        }
    }
}

Działa jak Python:

  • 0.To(4)[ 0, 1, 2, 3 ]
  • 4.To(0)[ 4, 3, 2, 1 ]
  • 4.To(4)[ ]
  • 7.To(-3, -3)[ 7, 4, 1, -2 ]
 6
Author: Kache,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:43:43

Chciałbym mieć składnię niektórych innych języków, takich jak Python, Haskell, itp.

// Write the numbers 1 thru 7
foreach (int index in [1..7])
{
    Console.WriteLine(index);
}

Fortunatly, we got F # now:)

Co do C#, to będę musiał trzymać się metody Enumerable.Range.

 5
Author: Thomas Danecker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:44:53

@ Łukasz: Zaimplementowałem Twoją metodę rozszerzenia To() i użyłem do tego metody Enumerable.Range(). W ten sposób wychodzi trochę krócej i wykorzystuje tyle infrastruktury, jaką daje nam. NET, ile to możliwe:

public static IEnumerable<int> To(this int from, int to)
{ 
    return from < to 
            ? Enumerable.Range(from, to - from + 1) 
            : Enumerable.Range(to, from - to + 1).Reverse();
}
 5
Author: Thorsten Lorenz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:48:17

Myślę, że Range jest przydatny do pracy z pewnym zakresem inline:

var squares = Enumerable.Range(1, 7).Select(i => i * i);
Możecie się przejechać. Wymaga konwersji do listy, ale zachowuje kompaktowość, gdy tego chcesz.
Enumerable.Range(1, 7).ToList().ForEach(i => Console.WriteLine(i));

Ale poza czymś takim, użyłbym tradycyjnego for loop.

 4
Author: mcNux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-29 15:20:55

Jestem pewien, że każdy ma swoje osobiste preferencje (wielu wolałoby później tylko dlatego, że jest znane w prawie wszystkich językach programowania), ale jestem taki jak ty i zaczynam lubić foreach coraz bardziej, zwłaszcza teraz, gdy możesz zdefiniować zakres.

 3
Author: TheTXI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-27 13:39:57

Wyobrażam sobie, że mogą istnieć scenariusze, w których Enumerable.Range(index, count) jest jaśniejsze, gdy mamy do czynienia z wyrażeniami dla parametrów, zwłaszcza jeśli niektóre wartości w tym wyrażeniu są zmieniane w pętli. W przypadku for wyrażenie będzie oceniane na podstawie stanu po bieżącej iteracji, podczas gdy {[2] } jest oceniane z góry.

Poza tym, Zgadzam się, że trzymanie się for byłoby normalnie lepsze (bardziej znane/czytelne dla większej liczby osób... czytelny jest bardzo ważnym wartość w kodzie, która musi być zachowana).

 2
Author: jerryjvl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-27 13:56:20

Moim zdaniem sposób Enumerable.Range() jest bardziej deklaratywny. Nowe i nieznane ludziom? Oczywiście. Ale myślę, że to deklaratywne podejście przynosi te same korzyści, co większość innych funkcji języka LINQ.

 2
Author: MEMark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:52:03

Ściśle mówiąc, nadużywasz wyliczenia.

Enumerator zapewnia dostęp do wszystkich obiektów w kontenerze jeden po drugim, ale nie gwarantuje kolejności.

Można użyć wyliczenia, aby znaleźć największą liczbę w tablicy. Jeśli używasz go do znalezienia, powiedzmy, pierwszego niezerowego elementu, polegasz na szczegółach implementacji, o których nie powinieneś wiedzieć. W twoim przykładzie kolejność wydaje się być dla ciebie ważna.

Edit : mylę się. Jako Łukasz zaznaczone (patrz komentarze) bezpiecznie jest polegać na kolejności przy wyliczaniu tablicy w C#. Różni się to na przykład od użycia "for in" do wyliczenia tablicy w Javascript .

 1
Author: buti-oxa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-29 23:33:29

Zgadzam się, że w wielu (a nawet w większości przypadków) foreach jest znacznie bardziej czytelna niż standardowa for-pętla, gdy po prostu iteracja nad zbiorem. Jednak twój wybór użycia Enumerable.Range(index, count) nie jest mocnym przykładem wartości foreach over for.

Dla prostego zakresu zaczynającego się od 1, Enumerable.Range(index, count) wygląda dość czytelnie. Jednakże, jeśli zakres zaczyna się od innego indeksu, staje się mniej czytelny, ponieważ musisz poprawnie wykonać index + count - 1, aby określić, jaki będzie ostatni element. Na przykład...

// Write the numbers 2 thru 8
foreach (var index in Enumerable.Range( 2, 7 ))
{
    Console.WriteLine(index);
}

W tym przypadku zdecydowanie wolę drugi przykład.

// Write the numbers 2 thru 8
for (int index = 2; index <= 8; index++)
{
    Console.WriteLine(index);
}
 1
Author: Dustin Campbell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:51:19

I do like the foreach + Enumerable.Range podejście i używać go czasami.

// does anyone object to the new style over the traditional style?
foreach (var index in Enumerable.Range(1, 7))
Sprzeciwiam się nadużyciom w twojej propozycji. Doceniam var, ale, cholera, po prostu napisz int w tym przypadku! ;-)
 0
Author: xyz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-04 07:49:56