Basit türler için yeni sınırlar tanımlamanızı sağlayan bir programlama dili


19

Birçok dil gibi C++, C#ve Javasen gibi basit türlerini temsil eden nesneleri oluşturmasına izin integerveya float. Bir sınıf arayüzü kullanarak operatörleri geçersiz kılabilir ve bir değerin 100 iş kuralını aşıp aşmadığını kontrol etmek gibi bir mantık gerçekleştirebilirsiniz.

Bazı kuralları bu kuralları bir değişken / özellik ek açıklamaları veya nitelikleri olarak tanımlamak mümkün olup olmadığını merak ediyorum.

Örneğin, C#şunları yazabilirsiniz:

[Range(0,100)]
public int Price { get; set; }

Ya da belki içinde C++yazabilirsiniz:

int(0,100) x = 0;

Daha önce böyle bir şey görmedim, ancak depolamadan önce veri doğrulamasına ne kadar bağımlı olduğumuzu göz önüne alarak. Bu özelliğin dillere eklenmemiş olması tuhaf.

Bunun mümkün olduğu dillere örnek verebilir misiniz?


14
Ada böyle bir şey değil mi?
zxcdw

2
@zxcdw: Evet, Ada, bu tür "türler" için destek sağlayan ilk dildi (bildiğim gibi). Adlandırılmış kısıtlanmış veri türleri.
m0nhawk

4
Bağımlı olarak yazılan tüm diller bu yeteneğe sahip olacaktır. Bu tür sistemi içkin var en.wikipedia.org/wiki/Dependent_type bu dillerde bir tip olarak tanımlanır yanı herhangi ML bu tür özel bir türü oluşturabilir gerçekçi olsa data Bool = True | Falsesen söyleyebiliriz istediğini ve için data Cents = 0 | 1 | 2 | ...bir var "Cebirsel Veri Türleri" ne bakın (daha düzgün hindley-milner türleri olarak adlandırılmalıdır, ancak insanlar bunu tür çıkarımı ile karıştırır) en.wikipedia.org/wiki/Algebraic_data_type
Jimmy Hoffa

2
Adını verdiğiniz dillerin tamsayı aşırı ve düşük akışını nasıl ele aldıkları göz önüne alındığında, sessiz aşırı / düşük akışını korursanız, bu tür bir aralık sınırlaması kendi başına çok fazla değmez.

9
@StevenBurnap: Türler OO gerektirmez. Sonuçta typePascal'da bir anahtar kelime var . Nesne yönelimi, programlama dillerinin "atomar" özelliğinden ziyade bir tasarım modelidir.
wirrbel

Yanıtlar:


26

Pascal'ın alt aralık türleri vardı, yani bir değişkene uyan sayıların sayısını azalttı.

  TYPE name = val_min .. val_max;

Ada'nın da menzilleri var: http://en.wikibooks.org/wiki/Ada_Programming/Types/range

Wikipedia'dan ....

type Day_type   is range    1 ..   31;
type Month_type is range    1 ..   12;
type Year_type  is range 1800 .. 2100;
type Hours is mod 24;
type Weekday is (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday); 

ayrıca yapabilir

subtype Weekend is  Weekday (Saturday..Sunday);
subtype WorkDay is  Weekday (Monday..Friday);

Ve işte serinlediği yer

year : Year_type := Year_type`First -- 1800 in this case...... 

C'nin katı bir alt aralık türü yoktur, ancak kullanılan bit sayısını en aza indirmek için bit alanlarını kullanarak birini (en azından sınırlı) taklit etmenin yolları vardır. struct {int a : 10;} my_subrange_var;}. Bu, değişken içerik için bir üst sınır olarak çalışabilir (genel olarak şunu söyleyebilirim: bunun için bitfield kullanmayın , bu sadece bir noktayı kanıtlamak içindir).

Diğer dillerde rastgele uzunluktaki tamsayı türleri için bir çok çözüm kütüphane düzeyinde gerçekleşir, Ie C ++ şablon tabanlı çözümlere izin verir.

Değişken durumların izlenmesine ve buna bağlı iddiaların bağlanmasına izin veren diller vardır. Örneğin Clojurescript'te

(defn mytest 
   [new-val] 
   (and (< new-val 10)
        (<= 0 new-val)))

(def A (atom 0 :validator mytest))

İşlev mytest, koşulların karşılanıp karşılanmadığını a( reset!veya aracılığıyla swap!) değiştirdiğinde çağrılır . Bu, geç bağlama dillerinde subrange davranışı uygulamak için bir örnek olabilir (bkz. Http://blog.fogus.me/2011/09/23/clojurescript-watchers-and-validators/ ).


2
Bağımlı türler hakkında bir detay ekleyecekseniz de iyi olurdu, bu sorun bağımlı yazmanın tüm amacı ve sebebidir, en azından belirtilmelidir (ezoterik olsa bile)
Jimmy Hoffa

Bağımlı tipler ve tümevarımsal akıl yürütme / milner tipi çıkarım hakkında biraz bilgim var. Bunlarla çok az pratik yapıyorum. Cevabıma bilgi eklemek isterseniz, düzenlemekten çekinmeyin. Endüktif tanımla Peano Aksiyomları ve matematikteki sayı türleri hakkında bir şeyler ekleyecektim, ancak güzel bir ML veri örneği belki de daha değerli olabilir.
wirrbel

enum kullanarak C'de bir aralık türü kullanabilirsiniz
John Cartwright 23:13

1
enum tür int veya imzasız int afaik (derleyiciye özgü olduğunu düşünüyorum) ve bağlı kontrol değil.
wirrbel

Bundan daha serinleşiyor: aralıklı türler dizi bildirimlerinde ve for y in Year_Type loop ... arabellek taşmaları gibi sorunları ortadan kaldıran döngüler için kullanılabilir .
Brian Drummond

8

Ada ayrıca basit türler için sınırlar sağlayan bir dildir, aslında Ada'da doğruluk garantisi için programınız için kendi türlerinizi tanımlamak iyi bir uygulamadır .

type MyType1   is range    1 .. 100;
type MyType2   is range    5 .. 15;

myVar1 : MyType1;

DoD tarafından uzun süre kullanıldı, belki de hala ama şu anki kullanımının izini kaybettim.


2
Ada, güvenlik açısından kritik sistemlerde hala yaygın olarak kullanılmaktadır. Dili son zamanlarda, güvenilir ve bakımı kolay bir yazılım yazmak için dili mevcut en iyi hale getiren bir güncelleme var. Maalesef araç desteği (derleyiciler, IDE test çerçeveleri vb.) Pahalıdır ve çalışmayı zor ve verimsiz hale getirmek için geride kalmaktadır.
mattnz

Utanç, ilk kez kullandığımı hatırlıyorum ve kodun ne kadar net ve hatasız olduğuna hayran kaldım. Hala aktif olarak güncellendiğini duyduğuma sevindim, hala harika bir dil.
greedybuddha

@mattnz: GNAT, gcc paketinin bir parçasıdır ve hem ücretsiz hem de ücretli sürümlerde bulunur.
Keith Thompson

@keith: GNAT Derleyici ücretsizdir. IDE'ler ve çerçeveler hala pahalıdır ve işlevselliği yoktur.
mattnz

7

C ++ ' da aralık denetimli bir değer türünün nasıl oluşturulacağına ilişkin örnekler için C ++' daki değer türleri aralığını sınırlama konusuna bakın .

Yönetici özeti: Şu şekilde kullanabileceğiniz yerleşik minimum ve maksimum değerlere sahip bir değer türü oluşturmak için bir şablon kullanın:

// create a float named 'percent' that's limited to the range 0..100
RangeCheckedValue<float, 0, 100> percent(50.0);

Burada bir şablona bile ihtiyacınız yok; benzer etki için bir sınıf kullanabilirsiniz. Şablon kullanmak temel türü belirlemenizi sağlar. Ayrıca, percentyukarıdaki türün floatşablonun bir örneği değil, bir örneği olacağını belirtmek önemlidir . Bu, sorunuzun 'basit türler' yönünü karşılamayabilir.

Bu özelliğin dillere eklenmemiş olması tuhaf.

Basit tipler sadece - basittir. Genellikle en iyi şekilde doğrudan kullanmak yerine ihtiyacınız olan araçları oluşturmak için yapı taşları olarak kullanılırlar.


2
@JimmyHoffa Bir derleyicinin aralık dışı koşulları algılayabileceği bazı durumlar olsa da, aralık kontrolünün çoğunlukla çalışma zamanında yapılması gerekir. Derleyici, bir web sunucusundan indirdiğiniz değerin aralıkta olup olmayacağını veya kullanıcının bir listeye çok fazla kayıt ekleyip eklemeyeceğini veya her neyse bilemez.
Caleb

7

Niyetinizin bazı sınırlı biçimleri, Ek Açıklamalar ve Dinamik Proxy Kalıbı (Java ve C # 'da dinamik proxy'ler için yerleşik uygulamalar mevcuttur) aracılığıyla Java ve C #' da mümkün olan bilgimdir.

Java sürümü

Ek açıklama:

@Target(ElementType.PARAMETER)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface IntRange {
     int min ();
     int max ();
}

Proxy örneğini oluşturan Wrapper sınıfı:

public class Wrapper {
    public static Object wrap(Object obj) {
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new MyInvocationHandler(obj));
    }
}

Her yöntem çağrısında bypass işlevi gören InvocationHandler:

public class MyInvocationHandler implements InvocationHandler {
    private Object impl;

    public MyInvocationHandler(Object obj) {
        this.impl = obj;
    }

@Override
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
    Annotation[][] parAnnotations = method.getParameterAnnotations();
    Annotation[] par = null;
    for (int i = 0; i<parAnnotations.length; i++) {
        par = parAnnotations[i];
        if (par.length > 0) {
            for (Annotation anno : par) {
                if (anno.annotationType() == IntRange.class) {
                    IntRange range = ((IntRange) anno);
                    if ((int)args[i] < range.min() || (int)args[i] > range.max()) {
                        throw new Throwable("int-Parameter "+(i+1)+" in method \""+method.getName()+"\" must be in Range ("+range.min()+","+range.max()+")"); 
                    }
                }
            }
        }
    }
    return method.invoke(impl, args);
}
}

Kullanım için Örnek Arayüz:

public interface Example {
    public void print(@IntRange(min=0,max=100) int num);
}

Main-Yöntem:

Example e = new Example() {
    @Override
    public void print(int num) {
        System.out.println(num);
    }
};
e = (Example)Wrapper.wrap(e);
e.print(-1);
e.print(10);

Çıktı:

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.print(Unknown Source)
at application.Main.main(Main.java:13)
Caused by: java.lang.Throwable: int-Parameter 1 in method "print" must be in Range (0,100)
at application.MyInvocationHandler.invoke(MyInvocationHandler.java:27)
... 2 more

C # -Sürüm

Ek açıklama (C # olarak adlandırılan öznitelik):

[AttributeUsage(AttributeTargets.Parameter)]
public class IntRange : Attribute
{
    public IntRange(int min, int max)
    {
        Min = min;
        Max = max;
    }

    public virtual int Min { get; private set; }

    public virtual int Max { get; private set; }
}

DynamicObject Alt Sınıfı:

public class DynamicProxy : DynamicObject
{
    readonly object _target;

    public DynamicProxy(object target)
    {
        _target = target;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        TypeInfo clazz = (TypeInfo) _target.GetType();
        MethodInfo method = clazz.GetDeclaredMethod(binder.Name);
        ParameterInfo[] paramInfo = method.GetParameters();
        for (int i = 0; i < paramInfo.Count(); i++)
        {
            IEnumerable<Attribute> attributes = paramInfo[i].GetCustomAttributes();
            foreach (Attribute attr in attributes)
            {
                if (attr is IntRange)
                {
                    IntRange range = attr as IntRange;
                    if ((int) args[i] < range.Min || (int) args[i] > range.Max)
                        throw new AccessViolationException("int-Parameter " + (i+1) + " in method \"" + method.Name + "\" must be in Range (" + range.Min + "," + range.Max + ")");
                }
            }
        }

        result = _target.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, _target, args);

        return true;
    }
}

Örnek Sınıf:

public class ExampleClass
{
    public void PrintNum([IntRange(0,100)] int num)
    {
        Console.WriteLine(num.ToString());
    }
}

Kullanımı:

    static void Main(string[] args)
    {
        dynamic myObj = new DynamicProxy(new ExampleClass());
        myObj.PrintNum(99);
        myObj.PrintNum(-5);
    }

Sonuç olarak, Java'da çalışmak için böyle bir şey alabileceğinizi görüyorsunuz , ancak tamamen uygun değil, çünkü

  • Proxy sınıfı yalnızca arabirimler için örneklendirilebilir, yani sınıfınızın bir arabirim uygulaması gerekir
  • İzin Verilen Aralık yalnızca arayüz düzeyinde bildirilebilir
  • Daha sonra kullanım sadece başlangıçta ekstra çaba ile gelir (MyInvocationHandler, her örneklemede sarma) ve bu da anlaşılabilirliği biraz azaltır

İçinde DynamicObject sınıfının yetenekleri C # C # uygulamasında gördüğümüz gibi, arayüz kısıtlamayı kaldırmak. Ne yazık ki, bu dinamik davranış bu durumda statik tür güvenliğini kaldırır, bu nedenle dinamik proxy'de bir yöntem çağrısına izin verilip verilmediğini belirlemek için çalışma zamanı denetimleri gereklidir.

Bu kısıtlamalar sizin için kabul edilebilirse, bu daha fazla kazma için bir temel oluşturabilir!


1
teşekkürler, bu harika bir cevap. C # böyle bir şey mümkün mü?
Reactgular

1
Sadece bir örnek C # uygulaması eklendi!
McMannus

Just FYI: public virtual int Min { get; private set; }Kodunuzu önemli ölçüde kısaltacak güzel bir numara
BlueRaja - Danny Pflughoeft 21:13

2
Bu, Q'nun neyle ilgili olduğundan tamamen farklıdır, yaptığınızın nedeni temel olarak dinamiktir; ki bu sorunun bir tip istediği yerde yazmanın antitezi , aralığın bir tipte olduğu zaman fark, çalışma zamanı değil derleme zamanında uygulanır. Kimse çalışma zamanında aralıkların nasıl doğrulanacağını sormadı, derleme zamanında kontrol edilen tip sistemi tarafından doğrulanmasını istedi.
Jimmy Hoffa

1
@JimmyHoffa ah bu mantıklı. İyi bir nokta :)
Reactgular

2

Sıradağlar özel bir değişmez durumdur. Wikipedia'dan:

Bir değişmez bir programın çalıştırılması sırasında doğru olduğu güvenilemez bir durumdur.

Aralık [a, b], x> = a ve x <= b değişmezleriyle tip x değişkeni olarak bildirilebilir .Integer

Bu nedenle Ada veya Pascal alt aralık türleri kesinlikle gerekli değildir. Değişmezlerle tamsayı tipinde uygulanabilirler.


0

Bu özelliğin dillere eklenmemiş olması tuhaf.

Sınırlı türler için özel özellikler, C ++ ve güçlü yazım sistemleri olan diğer dillerde gerekli değildir.

C ++ 'da, hedefleriniz kullanıcı tanımlı türlerle nispeten basit bir şekilde karşılanabilir . Sınırlı türlerin arzu edildiği uygulamalarda, bunlar yeterli değildir . Örneğin, derleyicinin fiziksel birim hesaplamalarının doğru yazıldığını doğrulamasını ister, böylece hız / zaman bir ivme üretir ve ivme / zamanın karekökünü almak bir hız üretir. Bunu uygun şekilde yapmak, bir formülde görülebilecek her türü açıkça adlandırmadan bir tür sistemi tanımlama yeteneğini gerektirir. Bu C ++ ile yapılabilir .

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.