F'de İşlevsel Programlama Kavramlarına Giriş#

İşlevsel programlama, işlevlerin ve sabit verilerin kullanımını vurgulayan bir programlama stilidir. Türlenmiş işlevsel programlama, işlevsel programlamanın F# gibi statik türlerle birleştirildiği durumlardır. Genel olarak, işlevsel programlamada aşağıdaki kavramlar vurgulanmıştır:

  • Kullandığınız birincil yapılar olarak işlevler
  • Deyimler yerine ifadeler
  • Değişkenler üzerinde sabit değerler
  • Kesinlik temelli programlamaya göre bildirim temelli programlama

Bu seri boyunca F# kullanarak işlevsel programlamadaki kavramları ve desenleri keşfedeceksiniz. Bu arada, biraz da F# öğreneceksiniz.

Terminoloji

İşlevsel programlama, diğer programlama paradigmaları gibi, sonunda öğrenmeniz gereken bir sözlükle birlikte gelir. Her zaman göreceğiniz bazı yaygın terimler şunlardır:

  • İşlev - İşlev, giriş verildiğinde çıkış oluşturacak bir yapıdır. Daha resmi olarak, bir öğeyi bir kümeden başka bir kümeye eşler . Bu formalizm, özellikle veri koleksiyonları üzerinde çalışan işlevler kullanılırken birçok yönden somut bir şekilde kaldırılır. İşlevsel programlamada en temel (ve önemli) kavramdır.
  • İfade - İfade, kodda değer üreten bir yapıdır. F# dilinde bu değer bağlı veya açıkça yoksayılmalıdır. İfade, işlev çağrısıyla önemsiz bir şekilde değiştirilebilir.
  • Saflık - Saflık, işlevin bir özelliğidir, böylece dönüş değeri aynı bağımsız değişkenler için her zaman aynıdır ve değerlendirmesinin yan etkileri yoktur. Saf bir işlev tamamen bağımsız değişkenlerine bağlıdır.
  • Bilgi Saydamlığı - Bilgi Saydamlığı, bir programın davranışını etkilemeden çıkışlarıyla değiştirilebilen ifadelerin bir özelliğidir.
  • Değiştirilemezlik - Değişmezlik, bir değerin yerinde değiştirilemeyeceği anlamına gelir. Bu, yerinde değişebilen değişkenlerin aksinedir.

Örnekler

Aşağıdaki örneklerde bu temel kavramlar gösterilmektedir.

İşlevler

İşlevsel programlamada en yaygın ve temel yapı işlevidir. İşte tamsayıya 1 ekleyen basit bir işlev:

let addOne x = x + 1

Tür imzası aşağıdaki gibidir:

val addOne: x:int -> int

İmza şu şekilde okunabilir: "addOne adlandırılmış bir int değeri x kabul eder ve " int. Daha resmi olarak, addOnebir değeri tamsayı kümesinden tamsayı kümesine eşlemektir . Belirteç bu -> eşlemeyi gösterir. F# dilinde genellikle işlev imzasına bakarak ne yaptığını anlayabilirsiniz.

Peki imza neden önemli? Yazılan işlevsel programlamada, bir işlevin uygulanması genellikle gerçek tür imzasından daha az önemlidir! 1 değerini bir tamsayıya eklemesi addOne çalışma zamanında ilginçtir, ancak bir program oluştururken, bu işlevi gerçekten nasıl kullanacağınızı bildiren, kabul edip döndürdüğü int gerçektir. Ayrıca, bu işlevi doğru bir şekilde kullandığınızda (tür imzasıyla ilgili olarak), herhangi bir sorunun tanılanması yalnızca işlevin addOne gövdesi içinde yapılabilir. Bu, yazılan işlevsel programlamanın arkasındaki ivmedir.

İfadeler

İfadeler, bir değer olarak değerlendirilen yapılardır. Eylem gerçekleştiren deyimlerin aksine ifadeler, değer veren bir eylem gerçekleştirmeyi düşünebilirsiniz. İfadeler neredeyse her zaman deyimler yerine işlevsel programlamada kullanılır.

Önceki işlevini göz önünde bulundurun: addOne. gövdesi addOne bir ifadedir:

// 'x + 1' is an expression!
let addOne x = x + 1

İşlevin sonuç türünü tanımlayan bu ifadenin addOne sonucudur. Örneğin, bu işlevi oluşturan ifade, gibi farklı bir tür olacak şekilde stringdeğiştirilebilir:

let addOne x = x.ToString() + "1"

İşlevin imzası şu şekildedir:

val addOne: x:'a -> string

F# içindeki herhangi bir tür üzerinde çağrılabildiğindenToString(), türü x genel yapılmıştır (Otomatik Genelleştirme olarak adlandırılır) ve sonuç türü bir stringolur.

İfadeler yalnızca işlevlerin gövdeleri değildir. Başka bir yerde kullandığınız bir değeri üreten ifadeleriniz olabilir. Yaygın olarak kullanılanlardan biri:if

// Checks if 'x' is odd by using the mod operator
let isOdd x = x % 2 <> 0

let addOneIfOdd input =
    let result =
        if isOdd input then
            input + 1
        else
            input

    result

ifadesi if adlı resultbir değer üretir. İfadeyi işlevin gövdesi addOneIfOdd haline getirerek if tamamen atlayabilirsinizresult. İfadeler hakkında hatırlanması gereken önemli nokta, bir değer üretmeleridir.

Döndürülecek bir şey olmadığında kullanılan özel bir türü unitvardır. Örneğin, şu basit işlevi göz önünde bulundurun:

let printString (str: string) =
    printfn $"String is: {str}"

İmza şöyle görünür:

val printString: str:string -> unit

türü unit , döndürülmekte olan gerçek bir değer olmadığını gösterir. Bu, çalışma sonucunda döndürülecek bir değere sahip olmasa da "iş yapması" gereken bir yordamınız olduğunda kullanışlıdır.

Bu, eşdeğer if yapının bir deyim olduğu ve değer üretmenin genellikle değişkenlerin sessize alındığı kesin programlamaya karşı keskin bir karşıtlıktır. Örneğin, C# dilinde kod şu şekilde yazılabilir:

bool IsOdd(int x) => x % 2 != 0;

int AddOneIfOdd(int input)
{
    var result = input;

    if (IsOdd(input))
    {
        result = input + 1;
    }

    return result;
}

C# ve diğer C stili dillerin, ifade tabanlı koşullu programlamaya olanak tanıyan üçüncül ifadeyi desteklediğini belirtmek gerekir.

İşlevsel programlamada, değerleri deyimlerle sessize almak nadirdir. Bazı işlevsel diller deyimleri ve mutasyonu desteklese de, bu kavramların işlevsel programlamada kullanılması yaygın değildir.

Saf işlevler

Daha önce belirtildiği gibi saf işlevler şunlardır:

  • Her zaman aynı giriş için aynı değeri değerlendirin.
  • Yan etkisi yoktur.

Bu bağlamda matematiksel işlevleri düşünmek yararlı olur. Matematikte işlevler yalnızca bağımsız değişkenlerine bağlıdır ve herhangi bir yan etkisi yoktur. matematiksel işlevinde f(x) = x + 1değerinin f(x) değeri yalnızca değerine xbağlıdır. İşlevsel programlamadaki saf işlevler de aynı şekildedir.

Saf bir işlev yazarken, işlevin yalnızca bağımsız değişkenlerine bağımlı olması ve yan etkiyle sonuçlanmaması gerekir.

Genel, değiştirilebilir duruma bağlı olduğundan saf olmayan bir işlev örneği aşağıda verilmiştir:

let mutable value = 1

let addOneToValue x = x + value

İşlev addOneToValue açıkça belirsizdir, çünkü value herhangi bir zamanda 1'den farklı bir değere sahip olacak şekilde değiştirilebilir. İşlevsel programlamada genel bir değere bağlı olarak bu desenden kaçınılmalıdır.

Yan etkiyi gerçekleştirdiğinden saf olmayan bir işlevin başka bir örneği aşağıda verilmiştir:

let addOneToValue x =
    printfn $"x is %d{x}"
    x + 1

Bu işlev genel bir değere bağımlı olmasa da değerini programın çıkışına yazar x . Bunu yapmanın doğal olarak yanlış bir yanı olmasa da, işlevin saf olmadığı anlamına gelir. Programınızın başka bir bölümü, çıkış arabelleği gibi programın dışında bir şeye bağımlıysa, bu işlevin çağrılması programınızın diğer bölümünü etkileyebilir.

deyimini printfn kaldırmak işlevi saf hale getirir:

let addOneToValue x = x + 1

Bu işlev, deyimiyle printfn önceki sürümden doğal olarak daha iyi olmasa da, bu işlevin tüm yaptığı şeyin bir değer döndürmesini garanti eder. Bu işlevin birkaç kez çağrılması aynı sonucu verir: yalnızca bir değer üretir. Saflık tarafından verilen öngörülebilirlik, birçok işlevsel programcının çabaladığı bir şeydir.

Değiştirilemezlik

Son olarak, tipli işlevsel programlamanın en temel kavramlarından biri değişmezliktir. F# dilinde tüm değerler varsayılan olarak sabittir. Bu, açık bir şekilde değiştirilebilir olarak işaretlemediğiniz sürece bunların yerinde kapatılamayacağı anlamına gelir.

Pratikte sabit değerlerle çalışmak, programlama yaklaşımınızı "Bir şeyi değiştirmem gerekiyor" yerine "Yeni bir değer üretmem gerekiyor" şeklinde değiştirdiğiniz anlamına gelir.

Örneğin, bir değere 1 eklemek, var olan değerin sesini kapatmak yerine yeni bir değer oluşturmak anlamına gelir:

let value = 1
let secondValue = value + 1

F# dilinde aşağıdaki kod işlevin value sesini kapatmaz; bunun yerine bir eşitlik denetimi gerçekleştirir:

let value = 1
value = value + 1 // Produces a 'bool' value!

Bazı işlevsel programlama dilleri mutasyonu hiç desteklemez. F# dilinde desteklenir, ancak değerler için varsayılan davranış değildir.

Bu kavram veri yapılarına daha da genişlemektedir. İşlevsel programlamada, kümeler (ve çok daha fazlası) gibi sabit veri yapıları başlangıçta bekleyebileceğinizden farklı bir uygulamaya sahiptir. Kavramsal olarak, kümeye öğe ekleme gibi bir şey kümeyi değiştirmez, eklenen değere sahip yeni bir küme oluşturur. Bu, genellikle verilerin uygun gösteriminin sonuç olarak verebilmesi için bir değeri verimli bir şekilde izlemeye olanak tanıyan farklı bir veri yapısıyla gerçekleştirilir.

Değerler ve veri yapıları ile çalışma stili kritik öneme sahiptir. Bu stil, sizi bir şeyi değiştiren herhangi bir işlemi o şeyin yeni bir sürümünü oluşturur gibi işlemeye zorlar. Bu, eşitlik ve karşılaştırılabilirlik gibi öğelerin programlarınızda tutarlı olmasını sağlar.

Sonraki adımlar

Sonraki bölümde işlevler kapsamlı bir şekilde ele alınacak ve bunları işlevsel programlamada kullanabileceğiniz farklı yollar keşfedilecektir.

F# dilinde işlevlerin kullanılması işlevleri derinlemesine inceleyerek bunları çeşitli bağlamlarda nasıl kullanabileceğinizi gösterir.

Daha fazla bilgi

İşlevsel Düşünme serisi, F# ile işlevsel programlama hakkında bilgi edinmek için harika bir kaynaktır. İşlevsel programlamanın temellerini, kavramları göstermek için F# özelliklerini kullanarak pragmatik ve kolay okunur bir şekilde kapsar.