nesnem değiştirilebilirse, işlevsel programlama bağlamında ne ters gidebilir?


9

Değişmez nesneler gibi değişebilir nesnelerin faydalarının, paylaşılan ve yazılabilir durum nedeniyle çok iş parçacıklı programlamadaki sorunları gidermek için çok fazla zaman harcadığını görebiliyorum. Aksine, değiştirilebilir nesneler her seferinde yeni kopya oluşturmak yerine nesnenin kimliğiyle başa çıkmaya yardımcı olur ve böylece özellikle büyük nesneler için performans ve bellek kullanımını geliştirir.

Anlamaya çalıştığım bir şey, işlevsel programlama bağlamında değişebilir nesnelere sahip olmanın neyi yanlış yapabileceğidir. Bana söylenen noktalardan biri, işlevleri farklı sırada çağırmanın sonucunun deterministik olmadığıdır.

İşlev programlamasında değişken nesne kullanarak neyin yanlış gidebileceğinin çok açık olduğu gerçek somut bir örnek arıyorum. Temel olarak kötü ise, OO veya fonksiyonel programlama paradigmasına bakılmaksızın kötüdür, değil mi?

Kendi ifademin altında bu soruya cevap verdiğine inanıyorum. Ama yine de daha doğal hissetmek için bir örneğe ihtiyacım var.

OO, kapsülleme, polimorfizm gibi araçlar yardımıyla bağımlılığı yönetmeye ve daha kolay ve sürdürülebilir bir program yazmaya yardımcı olur.

İşlevsel programlama aynı zamanda sürdürülebilir kodun teşvik edilmesinin aynı nedenine sahiptir, ancak OO araçlarını ve tekniklerini kullanma ihtiyacını ortadan kaldıran bir stil kullanarak - bunlardan biri yan etkileri, saf işlevi vb.


1
@ Çoğu işlev dillerinin değişken değişkenlere izin verdiğini söyleyebilirim, ancak bunları kullanmayı farklı kılın, örneğin değişken değişkenlerin farklı bir türü vardır
jk.

1
Bence ilk paragrafınızda değiştirilemez ve değişebilir olabilir misiniz?
jk.

1
@jk., kesinlikle yaptı. Bunu düzeltmek için düzenlendi.
David Arno

6
@ Ruben Fonksiyonel programlama bir paradigmadır. Bu nedenle işlevsel bir programlama dili gerektirmez. Ve F # gibi bazı fp dilleri bu özelliğe sahiptir .
Christophe

1
@Ruben hiçbir spesifik ben Haskell Mvars düşünüyordum hackage.haskell.org/package/base-4.9.1.0/docs/... elbette farklı çözümler farklı dillere sahip veya IORefs hackage.haskell.org/package/base-4.11.1.0 /docs/Data-IORef.html Tabii ki her ikisini de monadlar içinden kullanabilirsiniz
jk.

Yanıtlar:


7

Bence en iyi OO yaklaşımıyla karşılaştırılarak gösteriliyor

örneğin, bir nesnemiz olduğunu söyle

Order
{
    string Status {get;set;}
    Purchase()
    {
        this.Status = "Purchased";
    }
}

OO paradigmasında yöntem verilere eklenir ve bu verinin yöntemle mutasyona uğraması mantıklıdır.

var order = new Order();
order.Purchase();
Console.WriteLine(order.Status); // "Purchased"

İşlevsel Paradigmada, işlevi sonuç olarak tanımlarız. Satın alınan bir düzen IS bir siparişe uygulanan alım fonksiyonunun sonucu. Bu, emin olmamız gereken birkaç şey anlamına gelir

var order = new Order(); //this is a 'new order'
var purchasedOrder = purchase(order); // this is a 'purchased order'
Console.WriteLine(order.Status); // "New" order is still a 'new order'

Order.Status == "Satın alındı" yı bekler misiniz?

Aynı zamanda fonksiyonlarımızın idempotent olduğunu ima eder. yani. onları iki kez çalıştırmak her seferinde aynı sonucu vermelidir.

var order = new Order(); //new order
var purchasedOrder = purchase(order); //purchased order
var purchasedOrder2 = purchase(order); //another purchased order
var purchasedOrder = purchase(purchasedOrder); //error! cant purchase an order twice

Sipariş satın alma fonksiyonu tarafından değiştirilirse, satın alınanOrder2 başarısız olur.

Bir şeyleri fonksiyonların sonucu olarak tanımlayarak, bu sonuçları gerçekte hesaplamadan kullanmamızı sağlar. Hangi programlama açısından ertelenmiş yürütme.

Bu kendi içinde kullanışlı olabilir, ancak bir fonksiyonun ne zaman gerçekleşeceğinden emin olmadığımızda ve bu konuda iyiyiz, paralel işleme bir OO paradigmasında yapabileceğimizden çok daha fazla yararlanabiliriz.

Bir işlevi çalıştırmanın başka bir işlevin sonuçlarını etkilemeyeceğini biliyoruz; böylece bilgisayarı, istediği kadar iş parçacığı kullanarak, istediği sırada yürütmek üzere bırakabiliriz.

Bir işlev girdisini değiştirirse, bu tür şeyler konusunda çok daha dikkatli olmalıyız.


Teşekkürler !! çok yararlı. Dolayısıyla, satın alma işleminin yeni uygulaması, Order Purchase() { return new Order(Status = "Purchased") } durumun salt okunur alan olması gibi görünecektir . ? Yine bu uygulama neden fonksiyon programlama paradigması bağlamında daha alakalı? Bahsettiğiniz faydalar OO programlamasında da görülebilir, değil mi?
rahulaga_dev

OO'da object.Purchase () öğesinin nesneyi değiştirmesini beklersiniz. Bunu değişmez hale getirebilirsiniz, ama o zaman neden tam bir Fonksiyonel paradigmaya geçmiyorsunuz
Ewan

Ben sorun nesne görselleştirmek zorunda çünkü ben doğa tarafından nesne odaklı saf c # geliştirici am. Peki, işlevsel programlamayı kucaklayan dilde söylediğiniz şey, satın alınan siparişi döndüren 'Satın Alma ()' işlevinin herhangi bir sınıf veya nesneye eklenmesini gerektirmez, değil mi?
rahulaga_dev

3
fonksiyonel c # yazabilirsiniz nesnenizi bir yapıya değiştirebilir, değişmez hale getirebilir ve bir Func yazabilirsiniz <Sipariş, Sipariş> Satın Alma
Ewan

12

Değişmez nesnelerin neden faydalı olduğunu anlamanın anahtarı, işlevsel kodda somut örnekler bulmaya çalışmaktan çok da uzak değildir. İşlevsel kodların çoğu işlevsel diller kullanılarak yazıldığından ve çoğu işlev dili varsayılan olarak değiştirilemediğinden, paradigmanın doğası, aradığınız şeylerin olmasını önlemek için tasarlanmıştır.

Sorulması gereken en önemli şey, değişmezliğin bu yararı nedir? Cevap, karmaşıklığı önler. Diyelim ki iki değişkenimiz var xve y. Her ikisi de değeri ile başlar 1. ygerçi her 13 saniyede bir iki katına çıkar. Her birinin değeri 20 gün içinde ne olacak? xolacak 1. Bu kolay. Çok ydaha karmaşık olduğu için çalışmak gayret gösterecektir . 20 gün içinde günün hangi saati? Yaz saatini hesaba katmam gerekir mi? Karmaşıklığı ykarşısında xçok daha adildir.

Ve bu gerçek kodda da gerçekleşir. Karışıma her mutasyona uğratan bir değer eklediğinizde, bu kodu yazmaya, okumaya veya hata ayıklamaya çalışırken kafanızda veya kağıt üzerinde tutmanız ve hesaplamanız için başka bir karmaşık değer haline gelir. Ne kadar karmaşık olursa, hata yapma ve hata verme şansınız o kadar artar. Kod yazmak zordur; Okunması zor; hata ayıklaması zor: kodun doğru olması zor.

Değişebilirlik kötü değil . Sıfır değişkenliğe sahip bir programın hiçbir sonucu olamaz, bu da oldukça işe yaramaz. Değişebilirlik ekrana, diske veya başka bir sonuca bir sonuç yazmak olsa bile, orada olması gerekir. Kötü olan gereksiz karmaşıklıktır. Karmaşıklığı azaltmanın en basit yollarından biri, varsayılan olarak değişmez şeyler yapmak ve performans veya işlevsel nedenlerden dolayı bunları yalnızca gerektiğinde değiştirilebilir hale getirmektir.


4
"Karmaşıklığı azaltmanın en basit yollarından biri, varsayılan olarak değişmez şeyler yapmak ve bunları yalnızca gerektiğinde değiştirilebilir hale getirmektir": Çok hoş ve özlü bir özet.
Giorgio

2
@DavidArno Açıkladığınız karmaşıklık, kodun akıl yürütmesini zorlaştırır. "Kod yazmak zor; okunması zor; hata ayıklaması zor; ..." derken buna da değindiniz. Değişmez nesneleri seviyorum, çünkü kodları sadece kendim değil, tüm projeyi bilmeden bakan gözlemciler hakkında akıl yürütmeyi çok daha kolay hale getiriyorlar.
sökme numarası 5

1
@RahulAgarwal, " Ama neden bu problem fonksiyonel programlama bağlamında daha belirgin hale geliyor ". Öyle değil. Bence FP FP'de sorun çok daha az belirgindir, çünkü FP değişmezliği teşvik eder, böylece sorunu önler.
David Arno

1
@djechlin, " ? Nasıl 13 İkinci örnek değişmez kodu ile analiz etmek daha kolay hale gelebilir O olamaz": ymutasyona zorundadır; bu bir gereklilik. Bazen karmaşık gereksinimleri karşılamak için karmaşık kodlara sahip olmamız gerekir. Belirtmeye çalıştığım nokta, gereksiz karmaşıklıktan kaçınılması gerektiğidir. Değişen değerler doğal olarak sabit olanlardan daha karmaşıktır, bu nedenle - gereksiz karmaşıklığı önlemek için - değerleri yalnızca gerektiğinde değiştirin.
David Arno

3
Değişebilirlik bir kimlik krizi yaratır. Değişkeninizin artık tek bir kimliği yok. Bunun yerine, kimliği şimdi zamana bağlı. Yani sembolik olarak, tek bir x yerine artık x_t bir ailemiz var. Bu değişkeni kullanan herhangi bir kodun zaman hakkında da endişelenmesi gerekecek ve cevapta belirtilen ekstra karmaşıklığa neden olacaktır.
Alex Vong

8

fonksiyonel programlama bağlamında neler ters gidebilir

İşlevsel olmayan programlamada yanlış gidebilecek şeyler: Kapsamlı programlama dillerinin icadından bu yana iyi bilinen bir hata nedeni olan istenmeyen, beklenmedik yan etkiler elde edebilirsiniz .

IMHO, fonksiyonel ve fonksiyonel olmayan programlama arasındaki tek gerçek farktır, fonksiyonel olmayan kodda, tipik olarak yan etkiler beklersiniz, fonksiyonel programlamada, yapmayacaksınız.

Temel olarak kötü ise, OO veya fonksiyonel programlama paradigmasına bakılmaksızın kötüdür, değil mi?

Tabii ki istenmeyen yan etkiler paradigmaya bakılmaksızın bir hata kategorisidir. Bunun tersi de doğrudur - kasıtlı olarak kullanılan yan etkiler performans sorunları ile başa çıkmaya yardımcı olabilir ve I / O ve harici sistemlerle uğraşma söz konusu olduğunda çoğu paradigma ne olursa olsun genellikle gerçek dünya programları için gereklidir.


4

Sorunuzu oldukça iyi gösteren bir StackOverflow sorusunu yanıtladım. Değişken veri yapıları ile ilgili temel sorun, kimlikleri yalnızca bir an için geçerlidir, bu yüzden insanlar kimliğin sabit olduğunu bildikleri koddaki küçük noktaya olabildiğince sıkıştırmaya eğilimlidirler. Bu belirli örnekte, for döngüsü içinde çok fazla günlük kaydı yapıyor:

for (elem <- rows map (row => s3 map row)) {
  val elem_str = elem.map(_.toString)

  logger.info("verifying the S3 bucket passed from the ctrl table for each App")
  logger.info(s"Checking on App Code: ${elem head}")

  listS3Buckets(elem_str(1), elem_str(2)) match {

    case Some(allBktsInfo) =>
      logger.info(s"App: ${elem_str head} provided the bucket name as: ${elem_str(3)}")
      if (allBktsInfo.exists(x => x.getName == elem_str(3))) {
        logger.info(s"Provided S3 bucket: ${elem_str(3)} exists")
        println(s"s3 ${elem_str(3)} bucket exists")
      } else {
        logger.info(s"WARNING: Provided S3 bucket ${elem_str(3)} doesn't exists")
        logger.info(s"WARNING: Dropping the App: ${elem_str.head} from backup schedule")
        excludeList += elem_str.head // If the bucket is invalid then we exclude from backup
        println(s"s3 bucket ${elem_str(3)} doesn't exists")
    }

    case None =>
      logger.info(s"WARNING: Provided S3 bucket ${elem_str(3)} doesn't exists")
      logger.info(s"WARNING: Dropping the App: ${elem_str.head} from backup schedule")
      excludeList += elem_str.head // If the bucket is invalid then we exclude from backup
}

Değişmezliğe alışık olduğunuzda, çok uzun süre beklerseniz veri yapısının değişmesi korkusu yoktur, böylece boş zamanlarınızda mantıksal olarak ayrı olan görevleri çok daha ayrıştırılmış bir şekilde yapabilirsiniz:

val (exists, missing) = rows partition bucketExists
missing foreach {row =>
  logger.info(s"WARNING: Provided S3 bucket ${row("s3_primary_bkt_name")} doesn't exist")
  logger.info(s"WARNING: Dropping the App: ${row("app")} from backup schedule")
}

3

Değişmez nesneler kullanmanın avantajı, eğer kişi onu incelerken belirli bir özelliğe sahip olan bir nesneye referans alırsa ve aynı özelliğe sahip bir nesneye başka bir koda referans vermesi gerektiğinde, başka kimsenin referansı almış olabileceği veya nesneye neler yapabileceklerine bakılmaksızın nesneye yapılan başvuru boyunca [nesneye başka kimsenin yapamayacağı bir şey olmadığından] veya alıcı nesneyi ne zaman inceleyebileceğinden [ özellikleri incelendiğinde de aynı olacaktır].

Aksine, birisine, alıcı incelendiğinde (alıcının kendisini değiştirmediğini varsayarak) belirli bir özelliğe sahip olan değişebilir bir nesneye referans vermesi gereken kod, alıcıdan başka hiçbir şeyin değişmeyeceğini bilmelidir. bu özellik veya alıcının bu özelliğe ne zaman erişeceğini ve alıcının son incelemesine kadar bu özelliği değiştirmeyeceğini bilin.

Genelde programlama için (sadece fonksiyonel programlama değil) değişmez nesneleri üç kategoriye ayırmak en yararlı olduğunu düşünüyorum:

  1. Referansla bile olsa hiçbir şeyin değiştirilmesine izin veremeyen nesneler. Bu tür nesneler ve bunlara yapılan referanslar değer gibi davranır ve serbestçe paylaşılabilir.

  2. Kendilerini sağlayacak nesneler referanslar herhangi bir kod maruz asla onlara referanslar, ancak sahiptir kod tarafından değiştirilmesi aslında bunları değiştirin. Bu nesneler değerleri kapsar, ancak yalnızca değiştirilmemeleri veya yapabilecekleri koda maruz kalmamaları için güvenilebilecek kodlarla paylaşılabilir.

  3. Değiştirilecek nesneler. Bu nesneler en iyi kapsayıcı olarak görüntülenir ve bunlara referans olarak tanımlanır .

Yararlı bir desen genellikle bir nesnenin bir kap oluşturması, daha sonra bir referans tutmamak için güvenilebilecek bir kod kullanarak doldurması ve daha sonra evrenin herhangi bir yerinde var olacak tek referansın kodunu asla değiştirmeyecek olmasıdır. doldurulduktan sonra nesne. Konteyner değişebilir tipte olsa da, (*) değişmezmiş gibi düşünülebilir, çünkü hiçbir şey aslında onu değiştirmeyecektir. Konteynere yapılan tüm referanslar, içeriğini asla değiştirmeyecek şekilde değiştirilemeyen ambalaj tiplerinde tutulursa, bu tür ambalajlar, içindeki veriler değiştirilemez nesnelerde tutuluyormuş gibi güvenli bir şekilde geçirilebilir, çünkü ambalajlara yapılan referanslar serbestçe paylaşılabilir ve incelenebilir. istediğin zaman.

(*) Çok iş parçacıklı kodda, herhangi bir iş parçacığının sargıya herhangi bir referans görmesi için kap üzerindeki tüm eylemlerin etkilerinin o iş parçacığına görünmesini sağlamak için "bellek engelleri" kullanmak gerekebilir, ancak bu sadece bütünlük için bahsedilen özel bir durum.


etkileyici cevap için teşekkürler !! Muhtemelen kafa karışıklığımın kaynağı, c # arka planından olduğum ve her yerde değişebilen nesnelerden kaçınarak söyleyen "fonksiyonel stil kodu yazmayı öğreniyorum" çünkü - ama işlevsel programlama paradigmasını kucaklayan dillerin teşvik edildiğini düşünüyorum ( zorlamanın kullanılması doğruysa) değişmezlik.
rahulaga_dev

@RahulAgarwal: Bir nesneye referanslar , anlamı aynı nesneye yapılan diğer referansların varlığından etkilenmeyen, aynı nesneye başka referanslarla ilişkilendirecek bir kimliğe sahip olan ya da hiçbiri ile ilişkilendirilmeyen bir değeri çevrelemek mümkündür . Gerçek sözcük durumu değişirse, o durumla ilişkilendirilmiş bir nesnenin değeri veya kimliği sabit olabilir, ancak her ikisi birden değişmeyebilir - biri değişmelidir. 50.000 dolar ne yapmalı.
supercat

1

Daha önce de belirtildiği gibi, değişebilir durumdaki sorun temel olarak, daha büyük yan etki sorununun bir alt sınıfıdır; burada bir işlevin dönüş tipi, işlevin gerçekte ne yaptığını kesin olarak tanımlamaz, çünkü bu durumda durum mutasyonu da yapar. Bu sorun, F * ( http://www.fstar-lang.org/tutorial/ ) gibi bazı yeni araştırma dilleri tarafından giderilmiştir . Bu dil , tür işlevine benzer bir Efekt Sistemi oluşturur ; burada bir işlev yalnızca türünü değil, aynı zamanda etkilerini de statik olarak bildirir. Bu şekilde, işlevi arayanlar, işlevi çağırırken durum mutasyonunun meydana gelebileceğinin farkındadır ve bu etki, arayanlarına yayılır.

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.