Currying is een functie die te vinden is in de meeste moderne programmeertalen. Het vertaalt een enkele functie met meerdere argumenten in een reeks functies met elk een argument. In essentie maakt dit het mogelijk om functies in variabelen op te slaan en functies te creëren die functies retourneren.
Hoewel het in eerste instantie een vreemd concept lijkt, is het een krachtige techniek die soms erg nuttig kan zijn. In deze zelfstudie laat ik je zien hoe je kunt profiteren van het curry-curriculum in Swift.
Voordat we onze eigen aangepaste functies definiëren, laat ik je eerst een eenvoudig voorbeeld zien van currying in Swift met behulp van klassen.
Open Xcode, maak een nieuwe iOS- of OS X-speelplaats en voeg de volgende code toe:
class Car var speed = 0 func accelerateBy (factor: Int) -> Int speed + = factor return speed let car1 = Car ()
We definiëren een basisklasse met één eigenschap en één exemplaarmethode of -functie. We maken ook een instantie van de klas, car1
. We kunnen de accelerateBy (_ :)
methode van onze Auto
bijvoorbeeld met de volgende code:
car1.accelerateBy (10)
Er is echter een andere manier om deze methode uit te voeren door het gebruik van functie-currying. In het bovenstaande voorbeeld roept u de methode rechtstreeks op car1
ondanks dat de methode feitelijk wordt gedefinieerd in de Auto
klasse. De methode die we hebben geschreven is niet specifiek voor de car1
bijvoorbeeld, maar eerder de Auto
klasse. Wanneer je deze methode noemt, is wat er echt gebeurt, dat Swift eerst naar de Auto
klasse, haalt het accelerateBy (_ :)
methode, vertelt de methode welke instantie wordt gebruikt en voert vervolgens een gewijzigde methode uit die specifiek is voor die instantie. Om je te laten zien hoe dit werkt, voeg je de volgende regel toe aan je speeltuin:
Car.accelerateBy (car1)
Wat we hier doen is toegang tot de accelerateBy (_ :)
methode van de Auto
class and passing, als een parameter, aan welke instantie we deze functie willen laten uitvoeren. In de zijbalk van uw speeltuin kunt u zien dat het resultaat van deze methode een andere functie is.
Wat dit (Functie) resultaat vertegenwoordigt is eigenlijk een nieuw accelerateBy (_ :)
methode specifiek voor de car1
aanleg. Deze geretourneerde functie is uniek omdat deze specifiek verwijst naar car1
's snelheid
eigenschap in plaats van de generieke methode die u eerder hebt gedefinieerd, die kan verwijzen naar de snelheid
eigendom van een Auto
aanleg.
Zoals je zou verwachten, kunnen we nieuwe parameters doorgeven aan deze geretourneerde functie om deze uit te voeren zoals we gewoonlijk doen. Voeg de volgende regel code toe aan je speeltuin:
Car.accelerateBy (car1) (10)
Met deze code gaan we voorbij 10
als een parameter in het unieke accelerateBy (_ :)
methode en krijgen car1
huidige snelheid geretourneerd als resultaat.
Gefeliciteerd! U hebt net voor het eerst gebruikgemaakt van de functie die in Swift curryt.
Naast het samen currying van meerdere functies, de Fzalving resultaat dat uw code terugkeert, kan ook in een variabele worden opgeslagen. Hiermee kunt u het bestand opslaan en snel opnieuw gebruiken accelerateBy (_ :)
methode specifiek voor uw car1
aanleg. Voeg de volgende regels toe aan je speeltuin:
laat a = Car.accelerateBy (car1) a (10) a (20) a (30)
Je kunt die variabele zien een
gedraagt zich nu als elke andere globaal gedefinieerde functie. Het voordeel is dat het specifiek is voor een bepaald Auto
bijvoorbeeld, die tijdens runtime kan worden gedefinieerd in plaats van compileertijd. In uw speeltuin ziet u de verwachte uitgangen die in de zijbalk worden weergegeven.
Ten slotte zal ik je laten zien hoe je een functie uit een andere functie kunt retourneren door het gedrag van de functie opnieuw uit te voeren Auto
klasse die we zojuist hebben bekeken. We gaan dit doen door een functie te definiëren die een andere functie retourneert. In uw code kan deze functie er ongeveer zo uitzien:
func someFunction (invoer: AnyObject) -> (AnyObject) -> AnyObject // Dingen doen om een functie terug te geven
We definiëren de someFunction (_ :)
functie, die een accepteert AnyObject
parameter en retourneert een andere functie die ook een accepteert AnyObject
parameter die een retourneert AnyObject
resultaat. Dit type functiedefinitie kan in het begin erg verwarrend en afschrikwekkend zijn. Om het te vereenvoudigen, kunnen we profiteren van de typealias
trefwoord in Snel om een nieuwe naam toe te wijzen aan een gegevenstype.
Voeg de volgende code toe aan je speeltuin:
typealias IntFunction = (Int) -> Int func acceleratieForCar (auto: Auto) -> IntFunction terug Car.accelerateBy (auto) laat nieuwA = acceleratieForCar (auto1) nieuwA (10)
Door het gebruiken van typealias
om de naam van te mappen IntFunction
naar de (Int) -> Int
gegevenstype, hebben we het vereenvoudigd accelerationForCar (_ :)
functiedefinitie. De (Int) -> Int
gegevenstype vertegenwoordigt eenvoudigweg een functie die een accepteert Int
parameter en retourneert een Int
waarde.
Deze nieuwe functie gebruikt het ingebouwde gedrag van de Auto
klasse om een terug te sturen IntFunction
object dat vervolgens kan worden opgeslagen in een variabele en kan worden gebruikt zoals we eerder deden.
Hoewel het ingebouwde functie-currying-gedrag in Swift erg handig is, wilt u misschien uw eigen functies maken die geen verband houden met klassen. Voor dit deel van de tutorial gaan we eerst het voorbeeld gebruiken van een functie die een ander getal vermenigvuldigt met een constante waarde.
Stel u voor dat u een functie wilt creëren die één ingangsnummer accepteert en dat met 5 vermenigvuldigt. Deze eenvoudige functie kan er als volgt uitzien:
func multiplyBy5 (a: Int) -> Int return a * 5
Stel je nu voor dat je een vergelijkbare functie nodig hebt, maar je hebt het nodig om te vermenigvuldigen met 10 in plaats van 5. Dan heb je een andere functie nodig om te vermenigvuldigen met 20. Hoewel je drie soortgelijke functies zou kunnen maken en ze een naam zou geven multiplyBy5
, multiplyBy10
, en multiplyBy20
, dit zou veel beter kunnen worden behandeld met behulp van functie-currying.
Voeg het volgende codefragment toe aan uw speeltuin:
func multiplyBy (a: Int) -> IntFunction func nestedMultiply (b: Int) -> Int return a * b return nestedMultiply vermenigvuldig (10) (20) laat multiplyBy5 = multiplyBy (5) vermenigvuldigenBy5 (4)
We definiëren de multiplyBy (_ :)
functie die een accepteert Int
als de enige parameter en retourneert een functie van het type IntFunction
, het gegevenstype dat we eerder in deze zelfstudie hebben gedefinieerd. In deze functie definiëren we een andere functie, nestedMultiply (_ :)
. We nestelen deze functie binnen de eerste zodat deze niet kan worden uitgevoerd buiten de scope van de multiplyBy (_ :)
functie en heeft toegang tot de een
invoerparameter.
De drie regels onder de functiedefinitie zijn eenvoudige voorbeelden van hoe u de functies samen kunt curryen.
Hoewel u functies voor currying kunt maken met geneste functies, kunt u ze ook maken met behulp van sluitingen of door meerdere reeksen parameters te definiëren. Voeg als voorbeeld het volgende codefragment toe aan uw speeltuin:
func add (a: Int) -> IntFunction return b in a + b let add2 = add (2) add2 (4) func subtract (a: Int) (b: Int) -> Int return b - a laten aftrekken5 = aftrekken (5) aftrekken5 (b: 8)
Zoals u kunt zien, kunnen deze twee nieuwe functies op dezelfde manier worden samengevoegd als alle andere functies in deze zelfstudie. De enige uitzondering is dat met de functie die is gedefinieerd met twee sets parameters, u de tweede parameter expliciet een naam moet geven.
Er is geen limiet aan het aantal niveaus van functiecurrying die u in uw code kunt implementeren. De volgende code toont een voorbeeld van drie curried-functies en hoe deze verschilt over een enkele functie met drie parameters.
functie vermenigvuldigen (a: Int, b: Int, c: Int) -> Int return a * b * c functie vermenigvuldigen (a: Int) -> (Int) -> IntFunction func nestedMultiply1 (b: Int) - > IntFunction func nestedMultiply2 (c: Int) -> Int return a * b * c return nestedMultiply2 return nestedMultiply1 vermenigvuldigen (4, 5, 6) vermenigvuldigen (4) (5) (6)
Het belangrijkste verschil tussen deze twee vermenigvuldigingsfuncties is dat de curried-versie effectief kan worden gepauzeerd en opgeslagen in een variabele nadat de eerste of tweede parameter is verwerkt. Dit maakt de functie heel gemakkelijk opnieuw te gebruiken en rond te gaan als een object.
Functiecurving in Swift is een moeilijk concept om te vatten, maar in wezen draait het om twee kernbegrippen:
Er zijn verschillende manieren om functies samen te curryen en, terwijl alleen de Int
gegevenstype werd gebruikt in deze zelfstudie, dezelfde processen kunnen worden gebruikt met elk gegevenstype in uw Swift-projecten. Hierdoor kunnen functies in variabelen worden opgeslagen en vele malen in uw code worden gebruikt.
Laat zoals altijd uw opmerkingen en feedback achter in de opmerkingen.