Vraag Het gebruik van trailing-sluiting in een for-in-lus


ik gebruik map() functie van array in for-in-lus, zoals deze:

let numbers = [2, 4, 6, 8, 10]

for doubled in numbers.map { $0 * 2 } // compile error
{
    print(doubled)
}

welke compileerfout produceert:

Gebruik van niet-opgeloste identifier 'verdubbeld'

Als ik echter een haakje voor zet map() functie werkt het prima. d.w.z.

for doubled in numbers.map ({ $0 * 2 })
{
    print(doubled)
}

Mijn vraag is, waarom zou de compiler het code blok van de volgfunctie en loop niet differentiëren, ervan uitgaande dat dit het probleem veroorzaakt?


10
2017-07-14 13:28


oorsprong


antwoorden:


Dit komt omdat er onduidelijkheid zou bestaan ​​over de context waarin de nalopende functie zou moeten werken. Een alternatieve syntaxis die werkt, is:

let numbers = [2, 4, 6, 8, 10]

for doubled in (numbers.map { $0 * 2 }) // All good :)
{
    print(doubled)
}

Ik zou zeggen dat dit waarschijnlijk is omdat de 'in'-operator een hogere prioriteit heeft dan trailing-functies.

Het is aan jou, waarvan je denkt dat het leesbaarder is.


9
2017-07-14 13:33



Trailing closure-grammatica genereert een bekende dubbelzinnigheid tussen een hoofdtekst van de uitspraak (in uw geval is dat zo for loop, maar dit is ook van toepassing op andere uitspraken) en de body van trailing closure. Hoewel het technisch mogelijk is om dit probleem op te lossen, compilerontwerpers hebben besloten om syntaxis van sluitingsluitingen te verbieden bij het controleren van delen van verschillende verklaringen op hoog niveau:

Hoewel het mogelijk zou zijn om te vertellen wat in sommige gevallen is bedoeld door het uitvoeren van willekeurige lookahead of door het uitvoeren van typecontrole tijdens het parseren, hebben deze benaderingen belangrijke consequenties voor de architectuur voor de compiler. Als zodanig hebben we ervoor gekozen om de parser eenvoudig te houden en dit niet toe te staan.

Om de aard van het conflict te begrijpen, overweeg dit voorbeeld:

for v in expr { /* code 1 */ } { /* code 2 */ }

De parser moet een keuze maken met betrekking tot code 1 blok. Het zou het kunnen gebruiken als een trailing afsluiting van expr en behandelen code 2 als het lichaam van de for loop, of gebruik code 1 als het lichaam van de for loop, tijdens de behandeling code 2 als een onafhankelijke groep uitspraken omsloten door accolades - een klassieker verschuiving - conflict verminderen.

Het oplossen van dergelijke conflicten is erg duur. In essentie moet uw parser vooruit blijven kijken door meer tokens, totdat slechts één interpretatie zinvol is, of de parser geen tokens meer heeft (in dat geval maakt het een willekeurige beslissing op de een of andere manier, waardoor de programmeurs hun programma moeten disambigueren wanneer die keuze is niet wat ze wilden).

Het toevoegen van haakjes verwijdert de dubbelzinnigheid. Voorstellen werden overwogen om de dubbelzinnigheid te verwijderen door een verplicht sleutelwoord toe te voegen om het besturingsgedeelte van de lus te scheiden van zijn lichaam, d.w.z.

// The syntax of rejected proposal
for doubled in numbers.map { $0 * 2 } do {
    print(doubled) //                 ^^
}

Een extra toevoegen do sleutelwoord "verbindt" het blok aan de linkerkant, indien aanwezig, aan de uitdrukking, en maakt het blok aan de rechterkant de body van de lusinstructie. Deze aanpak heeft een groot nadeel, omdat het een brekende verandering is. Daarom is dit voorstel verworpen.


8
2017-07-14 13:36



De syntaxis is dubbelzinnig (zie antwoord van dasblinkenlight). Voor een alternatieve syntaxis:

let numbers = [2, 4, 6, 8, 10]

numbers.map { $0 * 2 }.forEach {
    print(doubled)
}

of

let numbers = [2, 4, 6, 8, 10]
let doubledNumbers = numbers.map { $0 * 2 }

for doubled in doubledNumbers {
    print(doubled)
}

2
2017-07-14 13:40