foreach dla 2 (lub więcej) list

0

Witam

W związku z tym, że do iterowania po listach zaleca się pętlę foreach, chciałbym się dowiedzieć czy jest możliwe wykonanie poniższej operacji za pomocą tej pętli.

string[] tabelaStringów1 = { "zero", "jeden", "dwa", "trzy", "więcej niż trzy" };
            string[] tabelaStringów2 = { "początek", "jeden", "dwa", "trzy", "ogólnie więcej" };
            List<string> listaStringów1 = new List<string>(tabelaStringów1);
            List<string> listaStringów2 = new List<string>(tabelaStringów2);
            int zakres;
            if(listaStringów1.Count == listaStringów2.Count)
            {
                    zakres = listaStringów1.Count;
                for (int i = 0; i < listaStringów1.Count; i++ )
                {
                    if(listaStringów1[i] == listaStringów2[i])
                    {
                        Console.WriteLine("Wiersz: " + i.ToString() + " zgadza się");
                    }
                }
            }

Już dość długo próbuje znaleźć odpowiedź w google, ale bez skutku - wszystkie pokazane przykłady pokazują użycie tej pętli tylko i wyłącznie dla przeglądania wszystkich elementów jednej listy.

Czy znajdzie się ktoś na tyle obeznany w temacie, aby odpowiedzieć na to pytanie?

0

Jeśli koniecznie chcesz mieć to zrobione na foreachu:

string[] tabelaStringów1 = { "zero", "jeden", "dwa", "trzy", "więcej niż trzy" };
string[] tabelaStringów2 = { "początek", "jeden", "dwa", "trzy", "ogólnie więcej" };
List<string> listaStringów1 = new List<string>(tabelaStringów1);
List<string> listaStringów2 = new List<string>(tabelaStringów2);
int zakres;
if(listaStringów1.Count == listaStringów2.Count)
{
 zakres = listaStringów1.Count;
 int index = 0;
 foreach(string s in listaStringow1)
 {
   if(s == listaStringów2[index])
   {
     Console.WriteLine("Wiersz: " + index.ToString() + " zgadza się");
   }
   index++  
 }
}

Ale jak widzisz to bez sensu, bardziej czytelny jest kod który podałeś. Zresztą pętla for jest prawdopodobnie szybsza niż foraech: http://www.codeproject.com/KB/cs/foreach.aspx Pierwszy z góry komentarz do artykułu pokazuje czasową różnicę w wykonywaniu się obu pętli.

0

Pętla foreach jest do tego i tylko do tego na co nazwa wskazuje: iteracji po wszystkich elementach kolekcji. Pozwala na pozbycie się zmiennej indeksowej.
A nie do tego, by każdą pętlę for zastępywać karkołomną konstrukcją z foreachem. Wtedy równie „dobrze” można to napisać przy pomocy goto.

int index=0;
petla:
  //...
  index++;
if (index<listaStringów2.Count) goto petla;

Zresztą pętla for jest prawdopodobnie szybsza niż foraech
Robiłem kiedyś testy i wychodzi różnie (± kilka procent) dla różnych pętli.

0

Robiłem kiedyś testy i wychodzi różnie (±kilka procent) dla różnych pętli.

To chyba zależne jest w głównej mierze od kompilatora

0

Rozumiem, że chcesz po prostu znaleźć część wspólną dwu tablic?

0

Można użyć do tego potęgi C# 3.5 i LINQ:

var query = form ciag1 in tabelaStringów1 
            from ciag2 in tabelaStringów2
            where ciag1 == ciag2 
            select ciag1;

      foreach (string ciag in query)
      {
        Console.WriteLine("znaleziono: " + ciag);
      }

Jak dla mnie wygląda schludnie i elegancko :)

//edit: można użyć jeszcze funkcji Intersect, która jest częścią LINQ właśnie:

var q = tabelaStringów1.Intersect(tabelaStringów2);
0

Hej hej, chwilę się zagapiłem, a tu burza mózgów trwa.

Rozumiem, że chcesz po prostu znaleźć część wspólną dwu tablic?

Nie szukam części wspólnej dwóch tablic, ale dzięki za zainteresowanie. Po prostu chciałem się dowiedzieć, czy warto stosować foreach do iteracji po dwóch listach naraz.

Ale jak widzisz to bez sensu, bardziej czytelny jest kod który podałeś. Zresztą pętla for jest prawdopodobnie szybsza niż foraech:

Dzięki za tą odpowiedź Adams85, właśnie takiej oczekiwałem. Zastanawiałem się kiedy warto ją stosować, a kiedy używać zwykłego for. Z tego co zrozumiałem, to foreach swoją konstrukcją ułatwia używanie każdego elementu w listach, a for (zagnieżdżony) służy do bardziej skomplikowanych operacji na nich.

Fajnie KubArek, że pokazałeś możliwości używania pętli w C# 3.5, już najwyższa pora, żebym zaczął używać Visual Studio 2010.

Jak zwykle można liczyć na ludzi z tego forum :)

0

foreach to "cukier składniowy", który umożliwia użycie mniejszej ilości kodu do iterowania po kolekcjach, które to umożliwiają, czyli implementują interfejs IEnumerator/IEnumerator<T>. A wiąże się to wszystko chyba z wzorcem Iterator.

Z angielskiej wiki:

// explicit version
IEnumerator<MyType> iter = list.GetEnumerator();
while (iter.MoveNext())
    Console.WriteLine(iter.Current);
 
// implicit version
foreach (MyType value in list)
    Console.WriteLine(value);

LukTar napisał(a)

Fajnie KubArek, że pokazałeś możliwości używania pętli w C# 3.5, już najwyższa pora, żebym zaczął używać Visual Studio 2010.

A teraz na 2005 siedzisz? Bo .NET Framework 3.5 był dostarczany z VS 2008.
No i nie ma czegoś takiego jak C# 3.5.
A tak w ogóle, to: http://www.maciejaniserowicz.com/post/2008/03/01/Numerologia-w-NET.aspx

0

Pomyliłem się bo nie używałem Visual Studio 2010 i myślałem, że jak zwykle podnieśli wersję C# i podnieśli, tylko nie do 3.5, ale 4.0. Nie zmienia to faktu, że nie odpowiadasz o to, o co zapytałem.

Dobrze znam definicję foreach, a pytanie nie brzmiało "skopiuj i wklej definicję foreach z Wikipedii", tylko czy da się używać foreach do równoczesnej iteracji po dwóch (lub więcej) listach.

A wiąże się to wszystko chyba z wzorcem Iterator.

Nie chyba, tylko na pewno - to prosta odmiana wzorca iterator, który wywołuje wszystkie el. listy po kolei :)

0
LukTar napisał(a)

Visual Studio 2010.

VS2010 złe, niedobre. Muli, ociężałe, i zepsute C++/CLI.

0

var query = form ciag1 in tabelaStringów1
from ciag2 in tabelaStringów2
where ciag1 == ciag2
select ciag1;

  foreach (string ciag in query)
  {
    Console.WriteLine("znaleziono: " + ciag);
  }

Twój kod nie działa tak jak autora.

Kod autora szukał czy elementy się zgadzają na tych samych pozycjach.
Twój kod szuka wszystkich elementów występujących w dwóch kolekcjach.

0
LukTar napisał(a)

Pomyliłem się bo nie używałem Visual Studio 2010 i myślałem, że jak zwykle podnieśli wersję C# i podnieśli, tylko nie do 3.5, ale 4.0. Nie zmienia to faktu, że nie odpowiadasz o to, o co zapytałem.

Nieścisłości są po to, żeby je prostować.
Ja nie zacząłem offtopicu o Visual Studio w Twoim wątku, nie miej do mnie pretensji...

Dobrze znam definicję foreach, a pytanie nie brzmiało "skopiuj i wklej definicję foreach z Wikipedii", tylko czy da się używać foreach do równoczesnej iteracji po dwóch (lub więcej) listach.

Poziom Twojego pytania sugeruje, że nie wiesz czym jest pętla foreach, dlatego też pozwoliłem sobie krótko wyjaśnić czemu działa ona tak, a nie inaczej.
Skoro jej konstrukcja jest taka, że pozwala na operowanie na tylko jednej kolekcji, to znaczy, że nie pozwala na operowanie na wielu jednocześnie.
Gdybyś to wiedział, po prostu nie zadałbyś tego pytania, więc "dobrze znam" jest tutaj nie na miejscu.

Nie chyba, tylko na pewno - to prosta odmiana wzorca iterator, który wywołuje wszystkie el. listy po kolei :)

j/w

0

Dziwne pytanie. Pętla foreach jest po to, zeby nie trzeba bylo uzywac zmiennej indeksowej. W tym przypadku zmienna indeksowa jest potrzebna, więc proby uzywania foreach na sile sa bez sensu.

0
othello napisał(a)

Dziwne pytanie. Pętla foreach jest po to, zeby nie trzeba bylo uzywac zmiennej indeksowej. W tym przypadku zmienna indeksowa jest potrzebna, więc proby uzywania foreach na sile sa bez sensu.

Spotkałem się z opinią, że nie powinno się używać pętli for i korzystać z nowszych osiągnięć techniki np. z pętli foreach. Założenie tego tematu miało rozwiać moje wątpliwości co do funkcjonalności pętli for - i rozwiązało.
Wnioski:
Pętla foreach powinna być wykorzystywana wyłącznie do iteracji po całej liście (lub tym podobnej odmianie kolekcji)
Pętla for jest szybsza i mniej ograniczona.

Temat uważam za zamknięty.
Dziękuję wszystkim za pomoc.

Ps.
somekind nie mam do Ciebie pretensji.
Nie będę odpowiadał na Twoje zaczepki, bo i tak z tego nic nie wyjdzie.
Jasne, że masz rację w niektórych kwestiach (tak samo jak ja) ale udowadniasz ją w strasznie arogancki sposób.

0

W Scali można łączyć listy i iteratory:

package Main

object Main extends Application {
  val a = List("Ala", "ma", "kota", "i", "krzyczy")
  val b = List("Kot", "ma", "AIDS", "i", "mruczy")

  for (x <- a zip b zipWithIndex) {
    if (x._1._1 == x._1._2)
      println("Zgadzającym się słowem jest: " + x._1._1 + " na pozycji: #" + x._2)
  }
}

Może kolega miał nadzieję że w C# jest podobna funkcjonalność ;)

0
LukTar napisał(a)

Jasne, że masz rację w niektórych kwestiach (tak samo jak ja) ale udowadniasz ją w strasznie arogancki sposób.

Gdy napisałem miłego posta w temacie, to przyczepiłeś się, że nie jest na temat, również w arogancki sposób. Przestałem zatem być miły, bo i po co? ;]

0
donkey7 napisał(a)

Może kolega miał nadzieję że w C# jest podobna funkcjonalność ;)

No prawie - tak na prawdę nie znam tego języka, ale co do nadziei na podobną funkcjonalność masz rację. Właśnie o coś podobnego mi chodziło, a dokładnie o mechanizm podobny do tego (Python):

dane = ('a', 'b', 'c', 'd')
for i, value in enumerate(dane)
    print i, value

ale nie dla dwóch zmiennych (obiektów) w jednej kolekcji, ale dwóch zmiennych (obiektów) w dwóch osobnych kolekcjach. Coś co by działało jak to:

foreach(obiekt1, obiekt2 in lista1, lista2)
    if (obiekt1.Equals(obiekt2))
        //... coś tam

Chyba trochę przesadziłem :).

0

Oj donkey7, dlaczego tak mało funkcyjnie i brzydko?

list1.zip(list2).zipWithIndex.foreach {
  case ((s1, s2), i) => if (s1 == s2) println("Wiersz: " + i + " zgadza sie")
}
0

Przecież ten kompiluje się do tego samego :P Poza tym to twój kod jest brzydki :)

Dziwiłem się czemu Scala się burzy na guarda w ifie, a tu nawiasów nie dałem. Nowa wersja:

object Main {
  def main(args: Array[String]): Unit = {
    val a = List("Ala", "ma", "kota", "i", "krzyczy")
    val b = List("Kot", "ma", "AIDS", "i", "mruczy")

    for (((s1, s2), i) <- (a zip b zipWithIndex) if (s1 == s2))
      println("Zgadzającym się słowem jest: " + s1 + " na pozycji: #" + i)
  }
}

http://www.ideone.com/vaqf6

0

No tak już czytelniej, ale i tak nie ma potrzeby stosowania tego potworka, jakim jest for w Scali, otrzymujesz dodatkowy narzut w postaci wywołania filter (do którego jest if tłumaczony)... Poza tym pamiętaj, że ja haskellowiec jestem, sam jesteś brzydki ;P

W tym wypadku unikanie kropek i nawiasów to kiepski pomysł, pogarsza czytelność. Ekastyczność != obfuskacja.

Edit, to jest ładne (mogłoby był ładniejsze, ale mi się nie chce...):

module Main where
 
main = mapM_ putStrLn [ mkS i x | (i, x, y) <- zip3 [0..] xs ys, x == y ]
    where
      xs = [ "Ala", "ma", "kota", "i", "krzyczy" ]
      ys = [ "Kot", "ma", "AIDS", "i", "mruczy"  ]
 
      mkS i s = unwords [ "Zgadzającym się słowem jest:", s, "na pozycji:", show i ]
0

To już podpada pod fetyszyzm :P

Dla Javowca czy C#owca mój kod Scalowy jest bardziej przyjemny :P

0

Dla programisty Scali już niekoniecznie. Scala kładzie ogromny nacisk na programowanie funkcyjne i wychodzi to znacznie lepiej niż chociażby w taki śmieszny Ruby. For to praktycznie podpieprzona z Haskella monadyczna notacja do - korzysta się z tego kiedy konieczne. Tamto list comprehension z main równie dobrze można zapisać z jego pomocą (lista jest monadą):

mapM_ putStrLn (do (i, x, y) <- zip3 [0..] xs ys; guard (x == y); return (mkS i x))

albo krócej, znając właściwości operatora bind dla listy:

mapM_ putStrLn (do (i, x, y) <- zip3 [0..] xs ys; guard (x == y); [mkS i x])

Korzystając z forM_ (mapM_ o odwróconych argumentach z Control.Monad) i dodając zipWithIndex można to prawie Scalowo zrobić (i infiksowo):

zipWithIndex = flip zip [0..]

(do ((x, y), i) <- zipWithIndex (zip xs ys); guard (x == y); return (mkS i x)) `forM_` putStrLn

Co w Scali wygląda bardzo podobnie:

(for (((x, y), i) <- (xs zip ys zipWithIndex) if (x == y)) yield mkS(x, i)) foreach println

Programowanie imperatywne to zło konieczne, gdzie możliwe powinno zostać ono zastąpione deklaratywnym, dlatego chociażby LINQ potrafi operować na kolekcjach innych niż bazy danych, do tego wspiera leniwą ewaluację (Scalowe map, flatMap, filter etc. są zachłanne). O korzyściach z tak kochanego przez developerów Scali referential transparency nie muszę wspominać.

Dobra, dosyć offtopica, jakby co to można to przedyskutować w innym dziale (chociaż nie wróżę potencjalnemu wątkowi popularności).

0

Map, flatMap i filter nie są zachłanne na buforowanym iteratorze (tzn iterator pamiętający aktualny element).

Na kolokwium z monad w Haskellu (I/O) się kiedyś wyłożyłem i niezbyt mi się chce wracać. For w Scali to syntactic sugar dla takich ludzi jak ja i bardzo się z tego cieszę :D Te Twoje przykłady to przerost formy nad treścią :P A z drugiej strony wiem jak działa ten For Scalowy, więc nie widzę problemu.

0

Do to syntactic sugar dla monad w Haskellu, przekształcający średniki/odpowiednio wcięte linie w operatory >>= i >>, tak jak scalowy for na map itd. Przykłady przedstawiały po prostu (wspólną) mechanikę, normalnie bym tak nie napisał.

Co do buforowanych iteratorów - zgoda, ale domyślnie cała mechanika for jest zachłanna (trzeba w dodatkowe środki zainwestować), przetestuj sobie ten przykład:

scala> def sideInc(n: Int) = { println("inkrementuję: " + n); n + 1 }
sideInc: (n: Int)Int

scala> for (n <- (0 to 2).map(sideInc)) println("wynik: " + n)
inkrementuję: 0
inkrementuję: 1
inkrementuję: 2
wynik: 1
wynik: 2
wynik: 3

scala> for (n <- (0 to 4).toList.map(sideInc)) println("wynik: " + n)
[j/w]

Monada IO jest jedną z najprostszych, leniwe I/O jest kochane - readFile in >>= jakieśTransformacje >>= writeFile out wcale nie wczytuje listy z pliku, buforuje fragmenty i leniwie leci po znakach.

Szkoda, że Coyote nie implementuje przenoszenia postów...

0

Wystarczy zamienić toList na toIterator albo toStream :)

Chyba częściej zachłanna wersja jest szybsza niż leniwa.

Te Haskellowe listy są chyba niemutowalne i pewnie dlatego można bezpiecznie robić na nich leniwe operacje. Leniwość w przypadku np map na mutowalnej liście jest dość niebezpieczna.

0

W wielu wypadkach leniwa ewaluacja sprawdza się fenomenalnie - jeżeli potencjalnie tylko niewielka część operacji zostanie wykonana. O ile GHC póki co generuje wolniejszy kod niż analogiczny soft w C (a dzięki własnościom Haskella mógłby być efektywniejszy - pozwala na więcej przekształceń i znacznie lepszą globalną optymalizację) to miałem przyjemność zaimplementować w nim kilka algorytmów, które dzięki leniwej ewaluacji w wypadku średnim były znacznie szybsze niż ich odpowiedniki w C. Przy 'gwarantowanym' pełnym przeglądzie leniwa ewaluacja raczej nie ma racji bytu chociaż narzut jest relatywnie niewielki.

Miałeś kolokwia z Haskella i nie wiesz, że generalnie wszystkie typy poza funkcjonującymi w obrębie monady I/O i ew. State są niemutowalne? Przecież to jeden z wymogów programowania czysto funkcyjnego. Poza tym niemutowalność + bezstanowość bardzo przydają się przy przetwarzaniu równoległym, z jednej strony potencjalnie mniej operacji (co się zazwyczaj co najwyżej uśrednia do kosztu operacji zachłannych), z drugiej praktycznie brak locków, nawet przy dziesiątkach równolegle mielących wątków.

Tak, toIterator/toStream, ale tak jak mówiłem to dodatkowe środki. Generalnie najlepiej prawie wszystko robić polimorficznie, przyjmując po jak najmniej wymagającym traicie, wspólnym dla oczekiwanej kolekcji i iteratora. O ile pamiętam to streamów/iteratorów nie da się indeksować dowolnie, co znacznie ogranicza ich przydatność jeżeli chodzi o leniwą ewaluację.

0

Miałeś kolokwia z Haskella i nie wiesz, że generalnie wszystkie typy poza funkcjonującymi w obrębie monady I/O i ew. State są niemutowalne?

Obawiałem się że jednak wprowadzili stan mutowalny :P

Ehh, a w robocie trzeba dalej używać Javy ;( Mam nadzieję, że w Javie 7 będzie więcej funkcyjności.

Leniwe filtry i mapy powodują że wartości trzeba przeliczać przy każdym odwołaniu do elementu. Trochę to nieefektywne przy skomplikowanych obliczeniach. Scala pozwala wybrać między leniwością / zachłannością w dowolnym miejscu i jest spoko.

0
donkey7 napisał(a)

Obawiałem się że jednak wprowadzili stan mutowalny :P

Obawiam się, że się mylisz. Tylko jeden rodzaj tablic pozwala na update in-place, poza monadą IO/State (a i to z ograniczeniami). Tak to mutowalnych kontenerów pracujących pod wspomnianymi monadami jest sporo.

donkey7 napisał(a)

Leniwe filtry i mapy powodują że wartości trzeba przeliczać przy każdym odwołaniu do elementu. Trochę to nieefektywne przy skomplikowanych obliczeniach. Scala pozwala wybrać między leniwością / zachłannością w dowolnym miejscu i jest spoko.

Leniwa ewaluacja polega właśnie na tym, że wartość jest ewaluowana raz, za pierwszym razem kiedy jest potrzebna, potem zwracany jest już raz obliczony wynik. Zamiast referencji do wartości jest referencja do closure opisującego ewaluację, po zwartościowaniu referencję się podmienia (przynajmniej tak to powinno być zaimplementowane). To co ma Scala to namiastka leniwej ewaluacji, czasem mimo wszystko się przydaje, ale w bardzo ograniczonym stopniu.

0

Na int[] nie zrobisz leniwej ewaluacji, a optymalizacje kompilatora polegające na wciskaniu int[] zamiast List<Integer> powodują znaczny wzrost wydajności. Nie ma Haskella na JVMa ani .NET, a to go znacznie ogranicza :)

0

A tam znacznie, to po prostu głównie język natywny, do tego raczej akademicki. Na .NET jest F#, od biedy może być, chociaż osobiście nie go nie trawię, już prędzej ocamla (na którym F# bazuje). Tak, wolałbym żeby sięgnięto po Haskella przy tworzeniu F#, dodano po prostu odpowiednią monadę do operacji w .NET i w sumie tyle, ale stało się jak się stało (jeden z twórców Haskella robi w Microsoft Research).

1 użytkowników online, w tym zalogowanych: 0, gości: 1