2013-05-21 9 views
16

Sto guardando la soluzione da Token Replacement and Identification:Qualcuno può rompere questa espressione lambda per me?

string result = Regex.Replace(
    text, 
    @"\[%RC:(\d+)%\]", 
    match => dict[int.Parse(match.Groups[1].Value)]); 

E non sto capire come il MatchEvaluator era sovraccarico.

Ho compreso alcune espressioni lambda. Prende l'input match e poi cerca un numero intero dal dizionario?

Ma da dove viene il valore per match? e dove va il valore restituito da match => dict[int.Parse(match.Groups[1].Value)]);?

Modifica: alcuni di voi hanno menzionato Delegate. Sorprendentemente dopo tre anni di università per CS, non ho mai incontrato questo termine. Che cos'è Delegate e cosa fa in questa situazione specifica?

Ultima modifica: Ho provato a scrivere il mio delegato con il seguente codice Dove i miei gettoni sono in forma di [@someTokenName]

public void CreateScript(Dictionary<string,string> dictionary, string path) 
    { 
     //used in the regex to identify the string to replace 
     var pattern = @"\[@[0-9a-fA-F]+\]"; 
     //read in the file found at the path 
     var lines = File.ReadAllLines(path); 
     int lineCounter = 0; 
     foreach (string line in lines) 
     { 
      line = Regex.Replace(line, pattern, match => dictionary.TryGetValue((match.Groups[0].Value))); 
     } 

Ma io continuo a ricevere un `Impossibile convertire un'espressione lambda a tipo 'int' perché non è un tipo delegato. Qual è la differenza tra la riga che ho scritto e quella trovata nella soluzione?

+3

Un 'Delegate' è simile a un puntatore a funzione. IOW, è un riferimento a un metodo; ma tu determini in fase di esecuzione il metodo specifico a cui fa riferimento. Dai un'occhiata a http://msdn.microsoft.com/en-us/library/900fyy8e(v=vs.71).aspx – ThatBlairGuy

+0

Che ha chiarito perfettamente le cose – Wusiji

+1

Quindi, in primo luogo, il compilatore deve essere in grado di dedurre che il tuo L'espressione lambda corrisponde a "MatchEvaluator", il che significa che deve avere un "Match" come input e "String" come output. Tuttavia, 'dictionary.TryGetValue' restituisce un' Boolean' piuttosto che un 'String', quindi il compilatore ti urlerà perché i tipi restituiti non corrispondono. In questo caso dovresti semplicemente usare 'dictionary [match.Groups [0] .Value]'. Tuttavia, se vuoi essere elegante e verificare prima che il valore esista, scriverò un altro esempio in un momento. – sircodesalot

risposta

58

Questa è un'espressione regolare abbinata a una lambda, quindi un po 'più complicata della normale lambda. Ecco come fare:

Data:

String result = Regex.Replace(
    text, 
    @"\[%RC:(\d+)%\]", 
    match => dict[int.Parse(match.Groups[1].Value)]); 

(1) il testo - il testo che stiamo cercando attraverso.

(2) @"\[%RC:(\d+)%\]" - mezzi trovato nulla che assomiglia a "[% CR: {numero}%]" dove {number} è ovviamente un certo numero (dal \d significa "un numero", e \d+ significa "uno o più numeri in successione"). Si noti inoltre che lo {number} o \d+ è circondato da () come in (\d+). Questo è importante perché significa che il numero è un "gruppo", che ha portato alla nostra spiegazione di seguito. I gruppi sono un modo per estrarre la parte "utile" da un'espressione regolare. Cioè, non vogliamo l'intera partita, solo il valore numero.

(3) Quando si trova una corrispondenza, esegue questo: match => dict[int.Parse(match.Groups[1].Value)]);

Partiamo da questa parte: match => ..., che è effettivamente la stessa:

public String MatchEval(Match match) 
{ 

} 

ricordare che un'espressione lambda è essenzialmente solo una breve mano per una funzione regolare (tranne che il compilatore deduce il tipo per match e il suo tipo di ritorno in base al delegato per cui è in piedi - qui un MatchEvaluator - di più su questo in un momento). Qui, l'input match viene passato nell'espressione lambda come input. Quindi hai => che inizia il corpo della funzione simile a { } che vediamo nella nostra funzione MatchEval sopra. Di conseguenza, ogni volta che viene trovata una corrispondenza, il codice equivalente a questo blocco viene eseguito:

public String MatchEval(Match match) 
{ 
    // Here we grab the value from group (1) (the number in parentasis from our Regex) 
    return dict[int.Parse(match.Groups[1].Value)]; 
} 

In breve, ricorda che un lambda è solo notazione abbreviata per una funzione. Se si guarda la documentazione per Regex.Replace, vedrai che il lambda è in piedi in un MatchEvaluator che è definita come:

public delegate string MatchEvaluator(Match match); 

che si allinea con la nostra espansione funzione di cui sopra. In realtà, si potrebbe semplicemente scrivere:

String result = Regex.Replace(
    text, 
    @"\[%RC:(\d+)%\]", 
    MatchEval); 

(supponendo dict era accessibile da un metodo separato) e la funzione funzionerebbe lo stesso dimostrando che un lambda è solo una notazione più breve per una funzione completa.

Edit: quanto riguarda la seconda parte della sua domanda, "Che cos'è un delegato", un delegato risolve essenzialmente il problema: "Non so quale funzione che voglio usare, ma so quello che firma esso ha". Considerate:

// This allows us to point to a math function with this signature, 
// namely, takes two Int32 inputs, and returns an Int32. 
public static delegate Int32 MathDelegate(Int32 lhs, Int32 rhs); 

public static Int32 Add(Int32 lhs, Int32 rhs) 
{ 
    return lhs + rhs; 
} 

// Note the variable names aren't important, just their TYPE 
public static Int32 Subtract(Int32 a, Int32 b) 
{ 
    return a - b; 
} 

static void Main() 
{ 
    // We can use a delegate to point to a "real" function 
    MathDelegate mathPerformer = Add; 

    Console.WriteLine(mathPerformer(2, 3)); // Output : 5 

    // Now let's point to "Subtract" 
    mathPerformer = Subtract; 

    Console.WriteLine(mathPerformer(2, 3)); // Output : -1 

    Console.ReadLine(); 
} 

Questo è utile quando non si sa cosa algoritmo specifico, o di tecnica di lavorazione che si desidera utilizzare fino il programma è già in esecuzione. Un delegato ci consente di scegliere a quale funzione puntare, e quindi possiamo eseguirlo mentre il programma è in esecuzione.

Il modo in cui tutto questo si riferisce alla discussione lambda di cui sopra, è che il MatchEvaluator non sa come gestire ciascuna delle corrispondenze che trova man mano che scorre nella stringa. Invece, fornendogli una funzione lambda, gli stai dicendo quale algoritmo vuoi usare quando viene trovata una corrispondenza. Fondamentalmente un delegato è utile per determinare in fase di esecuzione come si desidera eseguire un'azione.

Edit: Se si desidera espandere le vostre espressioni lambda per includere più di una 'linea' di codice, è possibile utilizzare un blocco di codice pure. Considerare:

String result = Regex.Replace(
    text, 
    @"\[%RC:(\d+)%\]", 
    match => { 
     return dict[int.Parse(match.Groups[1].Value)] 
    }); 

Qui noterete due cose diverse. (1) Il nostro => è ora seguito da { } che ci consente di inserire più righe di codice. Di conseguenza, tuttavia, il compilatore non sa quale valore è il valore restituito e quindi non può dedurre quale sia il tipo restituito. Pertanto, (2) inseriamo un comando esplicito return per indicare quale valore deve essere restituito.

Con questo semplice codice di base, potremmo fare qualcosa di simile:

String result = Regex.Replace(
    text, 
    @"\[%RC:(\d+)%\]", 
    match => { 
     // This does the same thing, but with more lines of code. 
     // Of course, you could get way more fancy with it as well. 
     String numericValueAsString = match.Groups[1].Value; 
     Int32 numericValue = Int32.Parse(numericValueAsString); 
     String dictionaryValue = dict[numericValue]; 

     // Same as above 
     return dictionaryValue; 
    }); 
+0

Ottima risposta !!! – DaveShaw

+0

Sorprendente risposta. Vorrei poterti dare più rappresentanti. – Wusiji

+2

Np. Sono felice di aiutare. – sircodesalot

3

Il lambda sta semplicemente definendo una funzione anonima. Questa funzione è passata al metodo Replace (in questo caso). Replace può quindi eseguire indipendentemente dal tipo desiderato con tale metodo. Dovresti controllare la documentazione per quel particolare metodo per vedere come sta usando il delegato fornito; avrete bisogno di contare da quello di dirvi dove il parametro viene, cosa fa con il valore di ritorno, ecc

La pagina di MSDN per tale metodo Stati, come la descrizione di quel parametro:

Un metodo personalizzato che esamina ogni corrispondenza e restituisce la stringa originale abbinata o una stringa sostitutiva.

Così sappiamo che, per ogni partita che si ritrova, si chiamerà questo metodo, passando l'oggetto Match che rappresenta la partita ha trovato come parametro. Utilizzerà quindi il valore di ritorno string (so che è una stringa perché lo definition of that delegate afferma così) di quel metodo come cosa sostituire la corrispondenza trovata con.

5

Letta in inglese:

Per tutti i gruppi corrispondenti dall'espressione, utilizzare il gruppo in corrispondenza dell'indice di valore 1 di, analizzare il valore come int e usarlo come/chiave indicizzatore per tirare un valore da a dictionaryobject chiamato dict da utilizzare come nuovo valore sostitutivo.

Il lambda è solo una funzione anonima che utilizza la corrispondenza come argomento ed esegue un corpo nel contesto dell'argomento.

7

Imagine:

string result = Regex.Replace(
text, 
@"\[%RC:(\d+)%\]", 
lambda); 

//public delegate string MatchEvaluator(Match match) 
string lambda(Match match) { 
    return dict[int.Parse(match.Groups[1].Value)]); 
} 

L'espressione lambda è lo stesso tipo del MatchEvaluator: il valore per match proviene dal regex (come se l'espressione lambda sono stati definiti come delegato regolare) e il valore della i ritorni delle espressioni lambda vengono assegnati a result, nuovamente, proprio come sarebbe se fosse definito come un delegato regolare.

Problemi correlati