[ mmix @ 08.01.2010. 17:32 ] @
Imam mali brain-freeze ili od sume ne vidim drvo.

Dakle imam multiline tekst i treba da uradim capture redova u kojima se nalazi 10 i vise cifara, ali ne izcela nego ukupno u redu

npr:

prvi red nema cifre
ovaj 2. red ima tacno 2 cifre
1234 e vode vec ima 5 cifara
1.2-3+4/5 e ovaj red mora da bude uhvacen 6-7=8-9-0


koji regex hvata ovo?
[ Igor Gajic @ 08.01.2010. 18:28 ] @
Ako ne mora RegEx moze i ovako:

Code:

line.ToCharArray().ToList().Where(s => s >= '0' && s <= '9').Count();


EDIT:

RegEx

Code:

Regex regex = new Regex("([0-9].*){10,}", RegexOptions.Compiled);


Prvi nacin je 15+ puta brzi....

[Ovu poruku je menjao Igor Gajic dana 08.01.2010. u 19:51 GMT+1]
[ mmix @ 10.01.2010. 19:42 ] @
A nismo se dobro razumeli, trebalo mi je da u regex ubacim ceo tekst odjednom i da dobijem capture grupu sa svim linijama koje zadovoljavaju uslov. Inace sam nasao resenje posle prespavane noci :)

Code:
^(?<linija>.*(\d.*){10})$


uz multiline flag.



[ Shadowed @ 10.01.2010. 21:10 ] @
Umm, ako je onaj prvi nacin brzi, zar ne mozes samo da uradis String.Split() pa nadovezes ono dato? Ili bas mora regex? :)
[ Igor Gajic @ 10.01.2010. 21:41 ] @
Pa i meni Regex deluje kao overkill.

Inace ovo se trivijalno moze resiti i kao:


Code:

List<string> linije = new List<string>();
foreach (string line in text.Split())
{
   int brojCifara = 0;

  for (int i = 0; i < line.Length; i++)
     if (line[i] >= '0' && line[i] <= '9') brojCifara++;

   if(brojCifara >=10) linije.Add(line);
}
[ Shadowed @ 10.01.2010. 21:55 ] @
Pa, ono sto si prvo dao radi manje/vise to unutar foreach-a. Jedino sto sto tamo nisi selektovao.
Bilo bi ovako nesto:

Code:
from str in podatak.Split("\n")
where str.ToCharArray().ToList().Where(s => s >= '0' && s <= '9').Count() >= 10
select str


Prelomih da bude citljivije.
Linq rocks :)
[ mmix @ 10.01.2010. 22:05 ] @
Problem jeste trivijalan, da je samo .NET ne bih pitao potreban mi je regex jer sinhronizujem vise sistema, .NET je samo jedan.
U osnovi imaju gomile tekstova koje moraju da audituju dok prolaze "checkpointe" a audit filteri su mi regexi.
I regex resenje je prosto samo mi mozak stao u tom trenutku

I da, Linq rocks Pokazah ti Luhn validator kao one liner u Linqu

Code:
string cardNumber = "5424000000000016"; // test
bool valid = cardNumber.ToList().ConvertAll(c => int.Parse(c.ToString())).AsEnumerable().
     Reverse().Aggregate(new {f = true, s = 0}, (o, i) => new {f = !o.f, s = o.s + (o.f ? i : (i*2)%9)}).s % 10 == 0;





[ Igor Gajic @ 10.01.2010. 22:16 ] @
Jeste isto kao i LINQ ali opet ostaje pitanje brzine, taj dodatak apstrakcije i konvertovanje u niz pa u listu oduzima vreme, pa je bare-metal resenje najbrze, ali LINQ je opet najcitkiji... Sve je pitanje kompromisa...
[ mmix @ 10.01.2010. 22:32 ] @
U pravu si, mada i sebe sve redje hvatam da jurim bare metal resenje za vecinu stvari. AKo pogledas i svoj primer i ti koristis Split() sto je u osnovi apstrakcija kao sto je foreach (koji ide kroz IEnumerable) gde bi najbrze resenje bilo skeniranje svih karaktera uz brojace i reset posle svakog \n.

Ono sto mene najvise zulja u LINQu je sto nema dovoljno ekstenzija nad "rinfuznim" tipovima sto forsira nepotrebne konverzije u druge rinfuzne tipove koji ih imaju, minimum bi IEnumeraable morao da ima sve ekstenzije i da ga svi rinfuzni tipovi publikuju, ne postoji npr nijedan dobar razlog da string ne daje IEnumerable<char> koji bi direktno cupao karaktere iz stringa bez string operacija. I ovaj moj luhn primer bi bio kraci i brzi da ne mora da se konvertuje u list, a btw shadowed mislim da tvoj linq kod moze krace; nemam VS sada ali bi trebalo da array ima where ekstenziju. Sem tih konverzija ako pogledas kako su linq operacije implementirane videces da i nemas mnogo manevarskog prostora do bare metal resenja, samo vise koda.
[ Igor Gajic @ 10.01.2010. 23:11 ] @
BTW:

Luhn validation, 2. nacin

Code:

            bool valid = 
                cardNumber
                .ToList()
                .Select(s => s-48)
                .Reverse()
                .Select((s,i)=> i%2 == 1 ? ((s*2)%9 == 0 ? 9 : (s*2)%9) : s)
                .Aggregate((x, s) => x + s) % 10 == 0;



LINQ je dusu dao za logicko razmisljanje i nalazenje alternativnih resenja...
[ Shadowed @ 11.01.2010. 00:12 ] @
Citat:
mmix: btw shadowed mislim da tvoj linq kod moze krace; nemam VS sada ali bi trebalo da array ima where ekstenziju.

Ja sam samo kopirao ono iz prve poruke :)
Ima Where ekstenziju.

Slazem se oko String-a. Zaista bi mogao malo vise.. da lici na List<Char> ili nesto slicno.
[ mmix @ 11.01.2010. 13:12 ] @
Svidja mi se ova fora sa select transformacijom, trazio sam nacin da dodjem do indeksa, nisam ni pomislio da select to daje. cool.

isti imam bug u mom LINQu sa 9-om na parnoj poziciji, i isto sam konacno nasao IEnumerabe<char> u stringu Nije uopste preko metoda vec sam string publikuje IEnumerable (d'oh). Ovo resenje dakle moze da se jos malo optimizuje i skrati:

- ne mora da se konvertuje u List<char>
- taj aggregate na kraju je u stvari prosti Sum
- u selct transformaciji moze da se smanji broj matematickih operacija, dovoljno je da se proveri >4 i da se koristi -9 umesto %9 nije neki benefit al ako vec jurimo "savrsenstvo"

Code:

            bool valid = cardNumber
                             .Select(c => c - 48)
                             .Reverse()
                             .Select((s, i) => i%2 == 1 ? (s > 4 ? s*2 - 9 : s*2) : s)
                             .Sum()%10 == 0;


inace, ovaj metod ne proizvodi ni jednu temp kopiju podataka, suma na kraju iterira kroz tri razlicita iteratora u jednom cugu nad stringom Apstraktno ili ne, mislim da je sasvim komparativno sa bare metal resenjem.

A inace, pricasmo io regexu
[ mmix @ 11.01.2010. 14:37 ] @
Evo ga jos jedan, malo drugacija kombinacija transformacija, kad vec prolazi samo jednom treba to iskoristiti. Trebalo bi videti koji je brzi

Code:
           bool valid = cardNumber
                             .Reverse()
                             .Select((c, i) => (c - 48) << (i & 1))
                             .Select(s => s > 9 ? s - 9 : s)
                             .Sum()%10 == 0;
[ Igor Gajic @ 11.01.2010. 15:39 ] @
Odlicno resenje

A krenuli smo od kobajle od LINQ-a...
[ mmix @ 11.01.2010. 18:11 ] @
ma meni samo zao sto select u query languagu nema varijantu koja eksponira index niti ima direktne agregacije, al bilo bi cool da sve ovo moze u "from d in cardnumber...."
[ Igor Gajic @ 11.01.2010. 19:32 ] @
LUHN LINQ testirano na 1e+6 16-bajtnih stringova:

originalno resenje: 5.61s
moje resenje: 2.59s
poslednje resenje: 1.91s

EDIT:

Bare-metal resenje se izvrsava za 0.016s (!)

Code:

                int sum = 0;
                for (int i = 16; i < 0; i--)
                {
                    int current = (cardNumber[i] - 48) << (i & 1);
                    if(current > 9) current -=9;
                    sum += current;
                }
                bool valid = sum % 10 == 0;


[Ovu poruku je menjao Igor Gajic dana 11.01.2010. u 20:47 GMT+1]
[ mmix @ 11.01.2010. 21:18 ] @
Imas pretpostavku da je card number 16 karaktera (sto btw ni u praksi ne mora biti tacno, amex ima 15 i luhn validiran je). Probaj sa cardNumber.length sa tim sto bi morao da promenis i&1 formulu jer moze da krene i od neparne pozicije. Mada ne verujem da ce to uticati mnogo na rezultat, ma koliko optimizovali query ipak su operacije enumeratora ustvari yield metode sa svim overhaedom istih, ne verujem da bi bilo koje LINQ resenje bilo brze.


U stvari bliza komparacija bi bila naspram foreach verzije.