İfade sorunu - Expression problem

İfadesi sorunu bir meydan okuma sorundur programlama dilleri statik olarak yazılan veriler soyutlamaların genişletilebilirliği ve modülerliği ile ilgilidir. Amaç, mevcut kodu yeniden derlemeden ve statik tip güvenliğini korurken (örneğin, yayın yok) veri soyutlamasına yeni temsiller ve yeni davranışlar eklenebileceği hem temsillerinde hem de davranışlarında genişletilebilir bir veri soyutlaması tanımlamaktır. . Programlama paradigmaları ve programlama dillerindeki eksiklikleri ortaya çıkardı ve önerilen birçok çözüm olmasına rağmen hala kesin olarak çözülmedi.

Tarih

Philip Wadler , Rice Üniversitesi'nin Programlama Dilleri Ekibi (PLT) ile yaptığı bir tartışmaya yanıt olarak zorluğu formüle etti ve "İfade Problemi" olarak adlandırdı . Ayrıca, meydan okumasının bağlamını tanımlayan üç kaynaktan alıntı yaptı:

Sorun ilk olarak 1975'te John Reynolds tarafından gözlemlendi . Reynolds iki Veri Soyutlama biçimini tartıştı: Artık Soyut Veri Tipleri (ADT'ler) olarak bilinen Kullanıcı Tanımlı Tipler ( Cebirsel Veri Tipleri ile karıştırılmamalıdır ) ve Prosedürel Veri Yapıları artık yalnızca bir yöntemle Nesnelerin ilkel bir biçimi olarak anlaşılan . Kullanıcı Tanımlı Türlerin yeni davranışlarla genişletilebileceğini ve Prosedürel Veri Yapılarının yeni temsillerle genişletilebileceğini, tamamlayıcı olduklarını savundu. Ayrıca 1967'ye kadar giden ilgili çalışmaları da tartıştı. Ancak, Reynold'un bu erken analize dayanan sonuçları tamamen yanlış çıktı: bir nesneye ikinci bir yöntem eklemenin "açık bir programlama örneğinden çok bir güç gösterisi olduğunu yazdı. " Nesne Yönelimli paradigmayı ve büyük başarısını tamamen kaçıran. Ayrıca, iki veri soyutlama biçiminin "doğal olarak farklı ve tamamlayıcı olduğuna" inanıyordu.

On beş yıl sonra 1990'da William Cook , Reynold'un ufuk açıcı fikrini, her ikisi de geniş çapta büyüyen Nesneler ve Soyut Veri Türleri bağlamında uyguladı. Cook, bir Veri Soyutlamada örtük olan temsiller ve davranışlar matrisini tanımladı ve ADT'lerin davranış eksenine, Nesnelerin ise temsil eksenine dayalı olduğunu tartıştı. Sorunla ilgili ADT'ler ve Nesneler üzerine kapsamlı bir çalışma tartışması sağlar. Ayrıca her iki stildeki uygulamaları gözden geçirdi, her iki yönde genişletilebilirliği tartıştı ve ayrıca statik yazmanın önemini belirledi. En önemlisi, yöntemlerin içselleştirilmesi ve optimizasyonu dahil olmak üzere, Reynolds'un düşündüğünden daha fazla esnekliğin olduğu durumları tartıştı.

ECOOP '98'de Shriram Krishnamurthi ve ark. ifade yönelimli bir programlama dilini ve onun araç setini aynı anda genişletme sorununa bir tasarım deseni çözümü sundu. Bunu "anlatım sorunu" olarak adlandırdılar çünkü programlama dili tasarımcılarının sorunu, yarattıkları şeylerin ifade gücünü göstermek için kullanabileceğini düşündüler. PLT için sorun, şimdi DrRacket olan DrScheme'in yapımında ortaya çıkmıştı ve bunu mixins'in yeniden keşfi yoluyla çözdüler . Programlama dilleri ile ilgili bir makalede programlama dili problemini kullanmaktan kaçınmak için Krishnamurthi ve ark. örüntü odaklı çözümlerini açıklamak için eski bir geometri programlama problemini kullandılar. ECOOP sunumundan sonra Felleisen ve Krishnamurthi ile yaptığı konuşmalarda Wadler, sorunun PL merkezli doğasını anladı ve Krishnamurthi'nin çözümünün Java'nın tip sistemini atlatmak için bir döküm kullandığına dikkat çekti. Tartışma, Corky Cartwright (Rice) ve Kim Bruce'un (Williams) OO dilleri için tip sistemlerinin bu dökümü nasıl ortadan kaldırabileceğini gösterdiği türler posta listesinde devam etti . Buna karşılık Wadler denemesini formüle etti ve "bir dilin ifade problemini çözüp çözemeyeceği, ifade kapasitesinin göze çarpan bir göstergesidir" sorusunu dile getirdi. "İfade sorunu" etiketi, ifade = "diliniz ne kadar ifade edebilir" ve ifade = "temsil etmeye çalıştığınız terimler dil ifadeleridir" kelimelerini kullanır.

Diğerleri, özellikle tezinde Thomas Kühne ve paralel bir ECOOP 98 makalesinde Smaragdakis ve Batory olmak üzere, Rice Üniversitesi'nin PLT'si ile aynı zamanlarda ifade probleminin varyantlarını birlikte keşfetti.

Bazı takip çalışmaları, programlama dili tasarımlarının gücünü sergilemek için ifade problemini kullandı.

İfade sorunu aynı zamanda çok boyutlu Yazılım Ürün Hattı tasarımında ve özellikle FOSD Program Küplerinin bir uygulaması veya özel durumu olarak temel bir sorundur .

Çözümler

İfade probleminin çeşitli çözümleri vardır. Her çözüm, bir kullanıcının bunları uygulamak için yazması gereken kod miktarına ve ihtiyaç duyduğu dil özelliklerine göre değişir.

Misal

Sorun Açıklaması

C# ile yazılmış ve genişletmek istediğimiz aşağıdaki kütüphanenin kaynak koduna sahip olmadığımızı düşünebiliriz :

public interface IEvalExp
{
    int Eval();
}

public class Lit: IEvalExp
{
    public Lit(int n)
    {
        N = n;
    }

    public int N { get; }

    public int Eval()
    {
        return N;
    }
}

public class Add: IEvalExp
{
    public Add(IEvalExp left, IEvalExp right)
    {
        Left = left;
        Right = right;
    }

    public IEvalExp Left { get; }

    public IEvalExp Right { get; }

    public int Eval()
    {
        return Left.Eval() + Right.Eval();
    }
}

public static class ExampleOne
{
    public static IEvalExp AddOneAndTwo() => new Add(new Lit(1), new Lit(2));
    public static int EvaluateTheSumOfOneAndTwo() => AddOneAndTwo().Eval();
}

Bu kitaplığı kullanarak 1 + 2 aritmetik ifadesini ExampleOne.AddOneAndTwo()' da yaptığımız gibi ifade edebilir ve .Eval() öğesini çağırarak ifadeyi değerlendirebiliriz . Şimdi bu kitaplığı genişletmek istediğimizi hayal edin, yeni bir tür eklemek çok kolay çünkü Nesne yönelimli bir programlama dili ile çalışıyoruz . Örneğin, aşağıdaki sınıfı oluşturabiliriz:

public class Mult: IEvalExp
{
    public Mult(IEvalExp left, IEvalExp right)
    {
        Left = left;
        Right = right;
    }

    public IEvalExp Left { get; }

    public IEvalExp Right { get; }

    public int Eval()
    {
        return Left.Eval() * Right.Eval();
    }
}

Ancak, tür üzerine yeni bir işlev eklemek istiyorsak (C# terminolojisinde yeni bir yöntem), IEvalExp arabirimini değiştirmemiz ve ardından arabirimi uygulayan tüm sınıfları değiştirmemiz gerekir. Diğer bir olasılık, IEvalExp arabirimini genişleten yeni bir arabirim oluşturmak ve ardından Lit , Add ve Mult sınıfları için alt türler oluşturmaktır , ancak ExampleOne.AddOneAndTwo() içinde döndürülen ifade zaten derlenmiştir, bu nedenle eski tip üzerinde yeni fonksiyon. Sorun, belirli bir türe işlev eklemenin kolay olduğu, ancak türleri genişletmenin veya eklemenin zor olduğu F# gibi işlevsel programlama dillerinde tersine çevrilir .

Nesne Cebirini Kullanarak Problem Çözümü

Kitleler için Genişletilebilirlik makalesindeki fikirleri kullanarak orijinal kitaplığı genişletilebilirliği göz önünde bulundurarak yeniden tasarlayalım .

public interface ExpAlgebra<T>
{
    T Lit(int n);
    T Add(T left, T right);
}

public class ExpFactory: ExpAlgebra<IEvalExp>
{
    public IEvalExp Lit(int n)
    {
        return new Lit(n);
    }

    public IEvalExp Add(IEvalExp left, IEvalExp right)
    {
        return new Add(left, right);
    }
}

public static class ExampleTwo<T>
{
    public static T AddOneToTwo(ExpAlgebra<T> ae) => ae.Add(ae.Lit(1), ae.Lit(2));
}

İlk kod örneğindekiyle aynı uygulamayı kullanıyoruz, ancak şimdi cebir için bir fabrikanın yanı sıra tür üzerindeki işlevleri içeren yeni bir arayüz ekliyoruz. Şimdi , doğrudan türler yerine ExpAlgebra<T> arabirimini kullanarak ExampleTwo.AddOneToTwo () içindeki ifadeyi oluşturduğumuza dikkat edin . Artık ExpAlgebra<T> arabirimini genişleterek bir işlev ekleyebiliriz, ifadeyi yazdırmak için işlevsellik ekleyeceğiz:

public interface IPrintExp: IEvalExp
{
    string Print();
}

public class PrintableLit: Lit, IPrintExp
{
    public PrintableLit(int n): base(n)
    {
        N = n;
    }

    public int N { get; }

    public string Print()
    {
        return N.ToString();
    }
}

public class PrintableAdd: Add, IPrintExp
{
    public PrintableAdd(IPrintExp left, IPrintExp right): base(left, right)
    {
        Left = left;
        Right = right;
    }

    public new IPrintExp Left { get; }

    public new IPrintExp Right { get; }

    public string Print()
    {
        return Left.Print() + " + " + Right.Print();
    }
}

public class PrintFactory: ExpFactory, ExpAlgebra<IPrintExp>
{
    public IPrintExp Add(IPrintExp left, IPrintExp right)
    {
        return new PrintableAdd(left, right);
    }

    public new IPrintExp Lit(int n)
    {
        return new PrintableLit(n);
    }
}

public static class ExampleThree
{
    public static int Evaluate() => ExampleTwo<IPrintExp>.AddOneToTwo(new PrintFactory()).Eval();
    public static string Print() => ExampleTwo<IPrintExp>.AddOneToTwo(new PrintFactory()).Print();
}

O Bildirim ExampleThree.Print () zaten derlenmiş bir ifade yazdırıyorsanız ExampleTwo , mevcut tüm kodunu değiştirmek gerek yoktu. Ayrıca bunun hala güçlü bir şekilde yazıldığına dikkat edin, yansıtmaya veya yayınlamaya ihtiyacımız yok. Biz yerini alacak olursa PrintFactory () ile ExpFactory () içinde ) ExampleThree.Print ( beri bir derleme hatası alacağı .print'i () metodu bu bağlamda yok.

Ayrıca bakınız

Referanslar

Dış bağlantılar