Fonksiyonel ve zorunlu programlama dilleri arasındaki fark nedir?


159

C #, Visual Basic, C ++ ve Java gibi nesne yönelimli programlama (OOP) dilleri de dahil olmak üzere ana akım dillerin çoğu öncelikli olarak zorunlu (yordamsal) programlamayı desteklemek üzere tasarlanmıştır, oysa Haskell / gofer benzeri diller tamamen işlevseldir. Herkes bu iki programlama yolu arasındaki farkın ne olduğunu açıklayabilir mi?

Programlamanın yolunu seçmenin kullanıcı gereksinimlerine bağlı olduğunu biliyorum, ancak neden fonksiyonel programlama dillerini öğrenmeniz önerilir?



1
diğer [gönderi] [1] 'e bakın. Farklılıkları açıkça tanımlar. [1]: stackoverflow.com/questions/602444/…
teta

Yanıtlar:


160

Tanım: Zorunlu bir dil, belirli bir hedefe nasıl ulaşılacağını belirlemek için bir dizi ifade kullanır. Bu ifadelerin, her biri sırayla yürütüldüğünde programın durumunu değiştirdiği söylenir.

Örnekler: Java zorunlu bir dildir. Örneğin, bir dizi sayı eklemek için bir program oluşturulabilir:

 int total = 0;
 int number1 = 5;
 int number2 = 10;
 int number3 = 15;
 total = number1 + number2 + number3; 

Her ifade, her değişkene değer atamaktan bu değerlerin son eklenmesine kadar programın durumunu değiştirir. Beş ifadeden oluşan bir sıra kullanılarak, programa açıkça 5, 10 ve 15 sayılarının nasıl ekleneceği açıklanmaktadır.

İşlevsel diller: İşlevsel programlama paradigması, problem çözme için saf işlevsel bir yaklaşımı desteklemek üzere açıkça oluşturulmuştur. Fonksiyonel programlama, bir tür bildirim programlamasıdır.

Saf Fonksiyonların Avantajları: Fonksiyonel dönüşümleri saf fonksiyonlar olarak uygulamanın temel nedeni saf fonksiyonların kompostlanabilir olmasıdır: yani müstakil ve vatansız. Bu özellikler aşağıdakiler de dahil olmak üzere bir dizi fayda sağlar: Artan okunabilirlik ve sürdürülebilirlik. Bunun nedeni, her işlevin bağımsız değişkenleri verilen belirli bir görevi gerçekleştirmek için tasarlanmış olmasıdır. Fonksiyon herhangi bir harici duruma bağlı değildir.

Daha kolay yinelenen geliştirme. Kodun yeniden düzenlenmesi daha kolay olduğundan, tasarımdaki değişikliklerin uygulanması genellikle daha kolaydır. Örneğin, karmaşık bir dönüşüm yazdığınızı ve daha sonra bazı kodların dönüşümde birkaç kez tekrarlandığını anlayın. Saf bir yöntemle yeniden düzenlerseniz, yan etkilerden endişe etmeden saf yönteminizi istediğiniz zaman çağırabilirsiniz.

Daha kolay test ve hata ayıklama. Saf işlevler ayrı ayrı daha kolay bir şekilde test edilebildiğinden, saf işlevi çağıran tipik kodlar, geçerli kenar durumları ve geçersiz kenar durumlarıyla test kodu yazabilirsiniz.

OOP Çalışanları veya Zorunlu diller için:

Nesneye yönelik diller, şeyler üzerinde sabit bir işlem kümeniz olduğunda iyidir ve kodunuz geliştikçe, öncelikle yeni şeyler eklersiniz. Bu, mevcut yöntemleri uygulayan yeni sınıflar eklenerek gerçekleştirilebilir ve mevcut sınıflar tek başına bırakılır.

İşlevsel diller, sabit bir şeyler kümeniz olduğunda iyidir ve kodunuz geliştikçe, öncelikle mevcut şeylere yeni işlemler eklersiniz. Bu, mevcut veri türleriyle hesaplanan yeni işlevler eklenerek gerçekleştirilebilir ve mevcut işlevler tek başına bırakılır.

Eksileri:

Programlama yolunu seçmek kullanıcının gereksinimlerine bağlıdır, bu nedenle sadece kullanıcılar uygun yolu seçmediğinde zarar vardır.

Evrim yanlış yola girdiğinde sorunlarınız olur:

  • Nesneye yönelik bir programa yeni bir işlem eklemek, yeni bir yöntem eklemek için birçok sınıf tanımının düzenlenmesini gerektirebilir
  • İşlevsel bir programa yeni bir tür şey eklemek, yeni bir vaka eklemek için birçok işlev tanımının düzenlenmesini gerektirebilir.

10
Bu durumda saf bir fonksiyon, matematiksel bir fonksiyonun eşdeğeridir. Aynı girişler her zaman aynı çıkışlarla eşlenir. Ayrıca herhangi bir yan etkisi yoktur (bir değer veya değer döndürmek dışında), derleyicinin bazı harika optimizasyonlar yapabileceği anlamına gelir ve uğraşacak hiçbir şey olmadığı için işlevi paralel olarak çalıştırmayı kolaylaştırır.
WorBlux

Peki, sürdürülebilir ve test edilebilir oop uygulamaları oluşturmanın doğru yolları ve en iyi uygulamaları, yavaşlayan bir zihinsel durum ile zorunlu kod tasarlama eğilimindedir?
Kemal Gültekin

4
Her bir programlamanın özelliğinin vurgulandığı metinde net bir fark görmüyorum. Prosedürel programlama için tarifin çoğu zorunlu programlama metni ile değiştirilebilir veya tersi de geçerlidir.
AxeEffect

7
Bu cevap, işlevsel programlamanın ne olduğunu açıklamaya çalışır, ancak saf bir işlevin ne olduğunu tanımlamak bile zahmet etmez. Kimsenin bu cevabı nasıl okuyabildiğini ve bildirimsel ve prosedürel programlama arasındaki farkı bilmek konusunda kendinden emin olduğunu göremiyorum.
Ringo

230

Fark şu:

Zorunlu:

  • Başlat
  • Ayakkabı bedeninizi 9 1/2 açın.
  • Bir dizi tuşu [7] tutmak için cebinizde yer açın.
  • Anahtarları cebinizdeki odaya yerleştirin.
  • Garaja girin.
  • Açık garaj.
  • Arabaya Gir.

... ve böyle devam ediyor ...

  • Sütü buzdolabına koyun.
  • Dur.

İşlevsel bir alt kategori olan bildirimsel:

  • Süt, laktozu sindirmede sorun yaşamadığınız sürece sağlıklı bir içecektir.
  • Genellikle, süt buzdolabında saklanır.
  • Buzdolabı, içindeki şeyleri serin tutan bir kutudur.
  • Mağaza, eşyaların satıldığı bir yerdir.
  • "Satış" demekle, şeylerin parayla değiş tokuşunu kastediyoruz.
  • Ayrıca, şeyler için para alışverişi "satın alma" olarak adlandırılır.

... ve böyle devam ediyor ...

  • Buzdolabında süt bulunduğundan emin olun (ihtiyaç duyduğumuzda - tembel fonksiyonel diller için).

Özet: Zorunlu dillerde, bilgisayara, belleğindeki bitleri, baytları ve kelimeleri nasıl ve hangi sırayla değiştireceğini söylersiniz. İşlevsel olanlarda, bilgisayara şeylerin, eylemlerin vb. Örneğin, 0 faktöriyelinin 1 olduğunu ve diğer tüm doğal sayıların faktöriyelinin, bu sayının ürünü ve selefinin faktöriyel olduğunu söylüyoruz. Biz şunu söylemiyoruz: n faktöriyelini hesaplamak için bir bellek bölgesi ayırın ve orada 1 depolayın, ardından o bellek bölgesindeki sayıyı 2 ile n arasındaki sayılarla çarpın ve sonucu aynı yerde ve sonunda saklayın, hafıza bölgesi faktöriyeli içerecektir.


1
Teşekkür ederim. Bu bakmak için harika bir yol.
L-Samuels

5
@Igno açıklamanızı beğendim, ama benim için hala bir şey belirsiz. Beyannamede, sadece bir şeyler söylemenize rağmen , yine de doğru ilerlemek için bitleri değiştirmeniz ve makinedeki durumlarda değişiklikler yapmanız gerekir. Bir şekilde Deklaratif'in Prosedürel Programlamaya (C Fonksiyonları gibi) benzediğini ve hala aralarında büyük bir fark olduğunu karıştırıyorum. C İşlevleri İşlevsel programlamadaki İşlevlerle aynı değil mi (Makine Düzeyinde)?
phoenisx

11
@Igno, Subroto gibi, açıklamanızı gerçekten anlamıyorum. Yazdıklarınız şu şekilde özetlenebilir gibi görünüyor: Cevap gerekiyor ... cevap alın. önemli bitleri görmezden geliyor gibi görünüyor. Bu parçayı kullanıcıdan nasıl gizleyebileceğinizi anlamıyorum, bir noktada birinin nasıl yapıldığını bilmesi gerekiyor ... sihirbazı perdenin arkasında sonsuza kadar tutamazsınız.
Brett Thomas

3
Bu işlevsel programlamanın ne olduğunu anladığım kadarıyla değil. İşlevsel programlamanın gizli girdilerin ve çıktıların işlevlerden kaldırılması olduğunu düşündüm.
Ringo

7
Kıvrımlı açıklama.
JoeTidee

14

Modern dillerin çoğu hem zorunlu hem de işlevsel olarak değişkendir, ancak fonksiyonel programlamayı daha iyi anlamak için, java / c # gibi çok işlevsel olmayan dilde zorunlu kodun aksine Haskell gibi saf işlevsel dile bir örnek almak en iyisi olacaktır. Örnekle açıklamanın her zaman kolay olduğuna inanıyorum, bu yüzden aşağıda bir tane var.

Fonksiyonel programlama: n yani n faktöriyelini hesaplayın! yani nx (n-1) x (n-2) x ... x 2 X 1

-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution  

factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3

-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1  
-- | 3 x (2 x (1 x (1)) = 6

Haskel'in fonksiyonun argüman değeri seviyesine aşırı yüklenmesine izin verdiğine dikkat edin. Şimdi, artan zorunluluk derecesinde zorunlu kodun bir örneği:

//somewhat functional way
function factorial(n) {
  if(n < 1) {
     return 1;
  }
  return n * factorial(n-1);   
}
factorial(3);

//somewhat more imperative way
function imperativeFactor(n) {
  int f = 1
  for(int i = 1; i <= n; i++) {
     f = f * i
  }
  return f;
}

Bu okuma , zorunlu kodun ne kadar parçaya, makinenin durumuna (döngü için i), yürütme sırasına, akış kontrolüne nasıl odaklandığını anlamak için iyi bir referans olabilir.

Daha sonraki örnek, kabaca java / c # lang kodu olarak görülebilir ve ilk kısım, Haskell'in aksine, işlevi değere (sıfır) aşırı yüklemenin aksine, dilin kendisinin sınırlandırılması olarak görülebilir ve diğer taraftan saf işlevsel dil olmadığı söylenebilir. el fonksiyonel prog destek söyleyebiliriz. bir dereceye kadar.

Açıklama: yukarıdaki kodların hiçbiri test edilmez / çalıştırılmaz, ancak umarım kavramı iletecek kadar iyi olmalıdır; Ayrıca ben böyle bir düzeltme için yorumlar takdir ediyorum :)


1
Olmamalı mı return n * factorial(n-1);?
jinawee

@jinawee, işaret ettiğin için teşekkürler, düzelttimn * (n-1)
old-monk

10

Fonksiyonel Programlama , hesaplama mantığını tanımlayan ve yürütme sırasını tamamen vurgulayan bir tür bildirim programlamasıdır.

Sorun: Bu canlıyı attan zürafaya değiştirmek istiyorum.

  • Boynu uzat
  • Bacakları uzat
  • Nokta uygulayın
  • Yaratığa siyah bir dil verin
  • At kuyruğunu kaldır

Her sonuç aynı sonucu elde etmek için herhangi bir sırayla çalıştırılabilir.

Zorunlu Programlama prosedüreldir. Devlet ve düzen önemlidir.

Sorun: Arabamı park etmek istiyorum.

  1. Garaj kapısının başlangıç ​​durumuna dikkat edin
  2. Araba yolunda durdur
  3. Garaj kapısı kapalıysa, garaj kapısını açın, yeni durumu hatırlayın; aksi halde devam et
  4. Arabayı garaja çek
  5. Garaj kapısını kapat

İstenilen sonuca ulaşmak için her adımın yapılması gerekir. Garaj kapısı kapalıyken garaja girmek, garaj kapısının kırılmasına neden olur.


Yalnızca zaman uyumsuz ve senkronizasyon arasındaki farkı görüyorum.
Vladimir Vukanac

@VladimirVukanac async / sync bir programlama şekli değil, bir mekanizmadır
Jakub Keller

2
Oh, teşekkürler, bunun hakkında daha fazla araştırma yapacağım. Sorun 1'i sorun 2 "Arabamı park etmek istiyorum" ile aynı olacak şekilde güncellemek ama işlevsel programlama yöntemiyle yazmak çok nazik misiniz? Sonra paralellik dışlanacak.
Vladimir Vukanac

6

Fonksiyonel programlama, bir fonksiyonun referans şeffaflığı da dahil olmak üzere beklenen bazı matematiksel özelliklere sahip olduğu "fonksiyonlarla programlama" dır. Bu özelliklerden başka özellikler, özellikle de matematiksel kanıtlara yol açan ikame edilebilirlik tarafından etkinleştirilen (yani bir sonuca güveni haklı çıkaran) tanıdık akıl yürütme adımları akar.

Sonuç olarak, işlevsel bir program sadece bir ifadedir.

İki stil arasındaki kontrastı, bir ifadenin artık referans olarak saydam olmadığı (ve dolayısıyla işlevler ve değerlerle oluşturulmadığı ve kendisinin bir işlevin parçası olamayacağı) zorunlu bir programdaki yerleri not ederek kolayca görebilirsiniz. En belirgin iki yer: mutasyon (örn. Değişkenler) diğer yan etkiler lokal olmayan kontrol akışı (örn. İstisnalar)

Fonksiyonlar ve değerlerden oluşan ifadeler olarak programlanan bu çerçevede, diller, kavramlar, "işlevsel kalıplar", birleştiriciler ve çeşitli tip sistemler ve değerlendirme algoritmaları gibi pratik bir paradigma inşa edilmiştir.

En uç tanıma göre, hemen hemen her dil - hatta C veya Java bile - işlevsel olarak adlandırılabilir, ancak genellikle insanlar özel olarak ilgili soyutlamalara sahip diller (kapanışlar, değişmez değerler ve desen eşleştirme gibi sözdizimsel yardımcılar gibi) için dil ayırır. İşlevsel programlamanın kullanımı söz konusu olduğunda, işlevlerin kullanımını içerir ve herhangi bir yan etkisi olmadan kod oluşturur. kanıt yazarken kullanılır


3

2005'ten 2013'e kadar web geliştirmede zorunlu programlama stili uygulandı.

Zorunlu programlama ile, uygulamamızın tam olarak ne yapması gerektiğini listeleyen kodu adım adım yazdık.

İşlevsel programlama stili, işlevleri birleştirmenin akıllı yollarıyla soyutlama üretir.

Cevaplarda bildirimsel programlamadan bahsediliyor ve bu konuda bildirimsel programlamanın izlememiz gereken bazı kuralları listelediğini söyleyeceğim. Daha sonra başvurumuza başlangıç ​​durumu olarak bahsettiğimiz şeyi sağlıyoruz ve bu kuralların uygulamanın nasıl davrandığını tanımlamasına izin veriyoruz.

Şimdi, bu hızlı açıklamalar muhtemelen pek bir anlam ifade etmiyor, bu yüzden zorunlu ve bildirimsel programlama arasındaki farkları bir benzetme yoluyla yürütelim.

Yazılım oluşturmadığımızı düşünün, bunun yerine yaşamak için turta pişiriyoruz. Belki de biz kötü fırıncıyız ve lezzetli bir turtayı nasıl yapmamız gerektiğini bilmiyoruz.

Patronumuz bize tarif olarak bildiğimiz yönlerin bir listesini veriyor.

Tarif bize nasıl pasta yapılacağını söyleyecektir. Bir tarif zorunlu bir tarzda yazılmıştır:

  1. 1 su bardağı un karıştırın
  2. 1 yumurta ekle
  3. 1 su bardağı şeker ekleyin
  4. Karışımı bir tavaya dökün
  5. Tavayı 30 dakika ve 350 derece F için fırına koyun.

Deklaratif tarif aşağıdakileri yapar:

1 su bardağı un, 1 yumurta, 1 su bardağı şeker - başlangıç ​​durumu

kurallar

  1. Her şey karışıksa, tavaya yerleştirin.
  2. Her şey karıştırılmamışsa, kaseye yerleştirin.
  3. Tavada her şey varsa, fırına yerleştirin.

Dolayısıyla zorunlu yaklaşımlar adım adım yaklaşımlarla karakterize edilir. Birinci adımdan başlayıp 2. adıma geçersiniz.

Sonunda bir son ürün elde edersiniz. Bu pastayı yaparak, bu malzemeleri karıştırıp bir tavaya ve fırına koyduk ve son ürününüzü aldık.

Deklaratif bir tarifte, tariflerimizde, tarifimizi değişkenler gibi başlangıç ​​durumunu listeleyen bir bölümle başlayarak iki ayrı parçaya ayıracağız. Yani buradaki değişkenlerimiz, içeriğimizin miktarları ve türleri.

İlk durumu veya başlangıç ​​bileşenlerini alır ve bunlara bazı kurallar uygularız.

Bu yüzden başlangıç ​​durumunu alıp ravent çilekli turta veya başka bir şey yemeye hazır olana kadar bu kurallardan tekrar tekrar geçiriyoruz.

Dolayısıyla, açıklayıcı bir yaklaşımda, bu kuralları nasıl doğru bir şekilde yapılandıracağımızı bilmeliyiz.

Bu nedenle, içeriklerimizi veya durumumuzu incelemek isteyebiliriz, eğer karıştırılırsa, bunları bir tavaya koyun.

Başlangıç ​​durumumuzla, bu eşleşmiyor çünkü malzemelerimizi henüz karıştırmadık.

Kural 2, eğer karıştırmazlarsa bir kasede karıştırın diyor. Tamam evet bu kural geçerlidir.

Şimdi bizim durumumuz olarak bir kase karışık malzememiz var.

Şimdi bu yeni durumu kurallarımıza tekrar uyguluyoruz.

Kural 1, eğer malzemeler karıştırılırsa bunları bir tavaya koyun, tamam evet şimdi kural 1 geçerli, hadi yapalım.

Şimdi, malzemelerin karıştırıldığı ve bir tavada karıştırıldığı bu yeni duruma sahibiz. Kural 1 artık geçerli değil, kural 2 geçerli değil.

Kural 3, malzemelerin bir tavada olup olmadığını, fırına koyun, bu yeni durum için geçerli olan kuralın harika olduğunu söyleyelim.

Ve lezzetli bir sıcak elmalı turta ya da her neyse.

Şimdi, eğer benim gibiyseniz, neden hala zorunlu programlama yapmıyoruz diye düşünüyor olabilirsiniz. Bu mantıklı.

Basit akışlar için evet, ancak çoğu web uygulamasının zorunlu programlama tasarımı ile düzgün şekilde yakalanamayan daha karmaşık akışları vardır.

Deklaratif bir yaklaşımda, bazı başlangıç ​​bileşenleri veya textInput=“”tek bir değişken gibi başlangıç ​​durumu olabilir .

Belki metin girişi boş bir dize olarak başlar.

Bu başlangıç ​​durumunu alıp uygulamanızda tanımlanan bir dizi kurala uygularız.

  1. Bir kullanıcı metin girerse, metin girişini güncelleyin. Nowu an için geçerli deđil.

  2. Şablon oluşturulmuşsa widget'ı hesaplayın.

  3. TextInput güncellenirse, şablonu yeniden oluşturun.

Bunların hiçbiri geçerli değil, bu yüzden program sadece bir olayın gerçekleşmesini bekleyecek.

Bu nedenle, bir noktada kullanıcı metin girişini günceller ve 1 numaralı kuralı uygulayabiliriz.

Bunu şu şekilde güncelleyebiliriz: “abcd”

Bu yüzden, sadece text ve textInput güncellemelerimizi güncelledik, kural numarası 2 geçerli değil, kural numarası 3, metin girişi güncellenmişse, yeni meydana geldiğini, sonra şablonu yeniden oluşturduğunu ve sonra şablon oluşturulduğunu söyleyen kural 2'ye geri dönüyoruz , widget hesaplamak, tamam widget hesaplamak sağlar.

Genel olarak, programcılar olarak, daha bildirimsel programlama tasarımları için çabalamak istiyoruz.

Zorunluluk daha açık ve açık görünmektedir, ancak açıklayıcı bir yaklaşım daha büyük uygulamalar için çok iyi ölçeklenmektedir.


2

• Zorunlu Diller:

  • Verimli uygulama

  • Karmaşık anlambilim

  • Karmaşık sözdizimi

  • Eşzamanlılık programcı olarak tasarlanmıştır

  • Karmaşık testler, referans şeffaflığı yoktur, yan etkileri vardır

  • Durumu var

• İşlevsel Diller:

  • Basit anlambilim

  • Basit sözdizimi

  • Daha az verimli yürütme

  • Programlar otomatik olarak eşzamanlı hale getirilebilir

  • Basit testler, referans şeffaflığı vardır, yan etkisi yoktur

  • Durumu yok

1

İşlevsel programlamayı zorunlu bir şekilde ifade etmenin mümkün olduğunu düşünüyorum:

  • Nesnelerin ve if... else/ switchifadelerin çok fazla durum denetimini kullanma
  • Eşzamansızlığa dikkat etmek için bazı zaman aşımı / bekleme mekanizması

Bu yaklaşımla ilgili büyük sorunlar var:

  • Kurallar / prosedürler tekrarlanır
  • Durumsallık yan etkiler / hatalar için şans bırakır

İşlevsel programlama, nesneler gibi işlevleri / yöntemleri tedavi etme ve vatansızlığı kucaklama, inandığım bu problemleri çözmek için doğdu.

Kullanım örnekleri: Android, iOS veya web uygulamalarının mantık dahil olmak üzere ön uç uygulamaları. arka uç ile iletişim.

Zorunlu / prosedür koduyla fonksiyonel programlamayı simüle ederken karşılaşılan diğer zorluklar:

  • Yarış kondisyonu
  • Karmaşık kombinasyon ve olaylar dizisi. Örneğin, kullanıcı bir bankacılık uygulamasında para göndermeye çalışır. Adım 1) Aşağıdakilerin tümünü paralel yapın, yalnızca her şeyin iyi olup olmadığını kontrol edin a) Kullanıcının hala iyi olup olmadığını kontrol edin (dolandırıcılık, AML) b) Kullanıcının yeterli bakiyesi olup olmadığını kontrol edin c) Alıcının geçerli ve iyi olup olmadığını kontrol edin (dolandırıcılık, AML) vb. Adım 2) aktarım işlemini gerçekleştirin Adım 3) Kullanıcının bakiyesinde ve / veya bir tür takipte güncellemeyi gösterin. Örneğin RxJava ile kod özlü ve mantıklıdır. Onsuz, bir sürü kod, dağınık ve hata eğilimli kod olacağını hayal edebiliyorum

Ayrıca günün sonunda fonksiyonel kodun derleyiciler tarafından zorunlu / prosedürel olan montaj veya makine koduna çevrileceğine inanıyorum. Ancak, derleme yazmadığınız sürece, insanlar yüksek düzeyde / insan tarafından okunabilir dille kod yazarken, işlevsel programlama listelenen senaryolar için daha uygun ifade yoludur.


-1

Bu sorunun daha eski olduğunu ve başkalarının zaten iyi açıkladığını biliyorum, aynı şeyi basit terimlerle açıklayan örnek bir sorun vermek istiyorum.

Sorun: 1'in tablosunu yazmak.

Çözüm: -

Zorunlu stile göre: =>

    1*1=1
    1*2=2
    1*3=3
    .
    .
    .
    1*n=n 

Fonksiyonel stile göre: =>

    1
    2
    3
    .
    .
    .
    n

Emir Kipi stilinde açıklamaları daha açık bir şekilde yazıyoruz ve daha basit bir şekilde adlandırılabilir.

İşlevsel tarzda olduğu gibi, kendini açıklayan şeyler göz ardı edilecektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.