간단한 유형에 대한 새로운 한계를 정의 할 수있는 프로그래밍 언어


19

대부분의 언어처럼 C++, C#그리고 Java당신은 같은 간단한 유형 나타내는 개체 만들 수 있습니다 integer또는 float. 클래스 인터페이스를 사용하면 연산자를 재정의하고 값이 비즈니스 규칙 100을 초과하는지 확인하는 것과 같은 논리를 수행 할 수 있습니다.

일부 언어에서 이러한 규칙을 변수 / 속성의 주석 또는 속성으로 정의 할 수 있는지 궁금합니다.

예를 들어 다음과 같이 C#작성할 수 있습니다.

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

아니면 C++당신은 쓸 수 있습니다 :

int(0,100) x = 0;

나는 이와 같은 일을 본 적이 없지만 스토리지 전에 데이터 유효성 검사에 얼마나 의존적인지를 감안할 때. 이 기능이 언어에 추가되지 않은 것은 이상합니다.

이것이 가능한 언어의 예를 들어 줄 수 있습니까?


14
에이다가 아닌가?
— zxcdw

2
@zxcdw : 예, Ada는 그러한 "유형"을 지원하는 최초의 언어입니다. 명명 된 제한된 데이터 유형.
— m0nhawk

4
의존적으로 유형이 지정된 모든 언어에는이 기능이 있습니다. 유형 시스템 en.wikipedia.org/wiki/Dependent_type 에는 본질적으로 본질적으로 모든 ML 에서이 성격의 사용자 정의 유형을 만들 수 있지만 이러한 언어에서는 유형이 정의되어 data Bool = True | False있으며 원하는 유형에 data Cents = 0 | 1 | 2 | ...대해 "대수 데이터 형식"(보다 적절하게 명명 된 hindley-milner 형식이지만 사람들이 형식 유추와 혼동 할 수 있음)을 참조하십시오. en.wikipedia.org/wiki/Algebraic_data_type
— Jimmy Hoffa

2
명명 된 언어가 정수 오버플로 및 언더 플로를 처리하는 방식을 고려할 때 자동 오버 / 언더 플로를 유지하는 경우 이러한 범위 제한은 그다지 가치가 없습니다.

9
@StevenBurnap : 유형에 OO가 필요하지 않습니다. type결국 파스칼 에는 키워드 가 있습니다 . 객체 지향은 프로그래밍 언어의 "atomar"속성보다 디자인 패턴에 가깝습니다.
— wirrbel

답변:


26

파스칼에는 하위 범위 유형이 있는데, 즉 변수에 맞는 수의 수가 감소합니다.

  TYPE name = val_min .. val_max;

Ada는 또한 범위의 개념을 가지고 있습니다 : http://en.wikibooks.org/wiki/Ada_Programming/Types/range

Wikipedia에서 ....

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); 

또한 할 수있다

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

그리고 여기가 시원 해지는 곳이 있습니다.

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

C에는 엄격한 하위 범위 유형이 없지만 사용되는 비트 수를 최소화하기 위해 비트 필드를 사용하여 하나를 모방하는 방법이 있습니다 (적어도 제한적 임). struct {int a : 10;} my_subrange_var;}. 이것은 가변 내용의 상한선으로 작동 할 수 있습니다 (일반적으로 나는 이것을 위해 필드를 사용하지 마십시오 . 이것은 포인트를 증명하는 것입니다).

다른 언어의 임의 길이 정수 유형에 대한 많은 솔루션이 라이브러리 수준에서 발생합니다. 즉, C ++는 템플릿 기반 솔루션을 허용합니다.

가변 상태를 모니터링하고 어설 션을 연결할 수있는 언어가 있습니다. 예를 들어 Clojurescript에서

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

(def A (atom 0 :validator mytest))

함수가 mytest될 때 호출된다 a(통해 변경 reset!또는 swap!조건이 충족되는지 여부를 확인). 이는 후기 바인딩 언어로 하위 범위 동작을 구현하기위한 예일 수 있습니다 ( http://blog.fogus.me/2011/09/23/clojurescript-watchers-and-validators/ 참조 ).


2
종속 유형에 대한 세부 사항을 추가하고 좋을 경우이 문제는 전체 유형과 종속 유형 지정의 이유입니다. (비밀 한 경우에도) 언급해야합니다.
— Jimmy Hoffa

의존적 유형과 귀납적 추론 / 밀러 유형 추론에 대해 약간의 이해가 있습니다. 나는 그들과 연습이 거의 없습니다. 내 답변에 정보를 추가하려면 자유롭게 편집하십시오. 귀납적 정의로 Peano Axioms와 숫자 유형에 대해 수학에 뭔가를 추가하려고했지만 멋진 ML 데이터 예제가 더 가치가있을 수 있습니다.
— wirrbel

enum을 사용하여 C에서 범위 유형을 끌 수 있습니다.
— John Cartwright

1
enum은 int 또는 unsigned int 유형의 컴파일러이며 (컴파일러 특정이라고 생각합니다) 바운드 검사되지 않습니다.
— wirrbel

그보다 더 시원합니다. 범위 선언 된 유형은 배열 선언 및 for y in Year_Type loop ... 버퍼 오버플로와 같은 문제 를 제거 하는 루프에 사용할 수 있습니다 .
— Brian Drummond

8

또한 Ada는 단순 유형에 대한 제한을 허용하는 언어입니다. 실제로 Ada에서는 정확성을 보장하기 위해 프로그램에 대한 고유 한 유형 을 정의하는 것이 좋습니다 .

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

myVar1 : MyType1;

그것은 DoD에 의해 오랫동안 사용되었지만 어쩌면 여전히 사용되지만 현재 사용중인 트랙을 잃어 버렸습니다.


2
Ada는 여전히 안전에 중요한 시스템에서 널리 사용됩니다. 최신 언어로 업데이트되어 현재 언어를 신뢰할 수 있고 유지 관리 가능한 소프트웨어를 작성하는 데 가장 적합한 언어 중 하나로 만듭니다. 불행히도 툴 지원 (컴파일러, IDE 테스트 프레임 워크 등)은 비용이 많이 들고 작업하기가 어렵고 비생산적입니다.
— mattnz

수치심, 나는 처음으로 그것을 사용하는 것을 기억하고 코드가 얼마나 명확하고 버그가 없는지 놀랐습니다. 여전히 활발히 업데이트되고 있으며 여전히 훌륭한 언어입니다.
— greedybuddha

@mattnz : GNAT는 gcc 제품군의 일부이며 무료 및 유료 버전으로 제공됩니다.
— Keith Thompson

@keith : GNAT 컴파일러는 무료입니다. IDE와 프레임 워크는 여전히 비싸고 기능이 부족합니다.
— mattnz

7

C ++ 에서 범위 확인 값 유형을 작성하는 방법에 대한 예제는 C ++에서 값 유형 범위 제한을 참조하십시오 .

요약 : 템플릿을 사용하여 최소값과 최대 값이 내장되어 다음과 같이 사용할 수있는 값 유형을 만듭니다.

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

여기에는 템플릿이 필요하지 않습니다. 비슷한 효과에 클래스를 사용할 수 있습니다. 템플릿을 사용하면 기본 유형을 지정할 수 있습니다. 또한 percent위 의 유형은 float템플릿이 아니라 템플릿 인스턴스 라는 점에 유의해야합니다 . 이것은 귀하의 질문의 '단순 유형'측면을 만족시키지 않을 수 있습니다.

이 기능이 언어에 추가되지 않은 것은 이상합니다.

단순한 유형은 간단합니다. 직접 사용하는 대신 필요한 도구를 만들기위한 빌딩 블록으로 사용하는 것이 가장 좋습니다.


2
@JimmyHoffa 컴파일러가 범위를 벗어난 조건을 감지 할 수있는 경우가 있다고 가정하지만 범위 검사는 주로 런타임에 수행해야합니다. 컴파일러는 웹 서버에서 다운로드 한 값이 범위 내에 있는지 또는 사용자가 목록에 너무 많은 레코드를 추가하는지 등을 알 수 없습니다.
— Caleb

7

당신의 의도의 일부 제한된 형태는 주석과 동적 프록시 패턴 (Java 및 C #의 동적 프록시에 대한 내장 구현이 있음)을 통해 Java 및 C #에서 가능한 지식입니다.

자바 버전

주석 :

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

프록시 인스턴스를 작성하는 래퍼 클래스 :

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

모든 메소드 호출에서 우회 역할을하는 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);
}
}

사용을위한 예제 인터페이스 :

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

주요 방법 :

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);

산출:

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 # 버전

주석 (C #에서 attribute라고 함) :

[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 하위 클래스 :

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;
    }
}

ExampleClass :

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

용법:

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

결론적으로 Java 에서 작동하는 것과 같은 것을 얻을 수 있지만 완전히 편리하지는 않습니다.

  • 프록시 클래스는 인터페이스를 위해 인스턴스화 할 수 있습니다. 즉, 클래스는 인터페이스를 구현해야합니다.
  • 허용 범위는 인터페이스 수준에서만 선언 할 수 있습니다
  • 나중에 사용법은 처음에 추가 노력 (MyInvocationHandler, 모든 인스턴스화에서 래핑)으로 인해 이해도를 약간 줄입니다.

C # 에서 DynamicObject 클래스의 기능은 C # 구현에서 볼 수 있듯이 인터페이스 제한을 제거합니다. 불행히도이 경우 동적 동작은 정적 유형 안전을 제거하므로 동적 프록시에서 메소드 호출이 허용되는지 여부를 판별하기 위해 런타임 검사가 필요합니다.

이러한 제한이 허용되는 경우 추가 파기를위한 기초가 될 수 있습니다!


1
고마워, 이것은 멋진 답변입니다. C #에서 이와 같은 것이 가능합니까?
— Reactgular

1
샘플 C # 구현을 추가했습니다!
— McMannus

FYI : public virtual int Min { get; private set; }코드를 대폭 단축 할 수있는 좋은 방법입니다.
— BlueRaja-Danny Pflughoeft

2
이것은 Q에 관한 것과는 완전히 다릅니다. 당신이하고있는 이유는 기본적으로 역학입니다. 이것은이 질문이 유형을 요구하는 타이핑의 대립입니다 . 범위가 유형에있을 때의 차이점은 런타임이 아닌 컴파일 타임에 시행됩니다. 런타임에 범위를 확인하는 방법에 대해 아무도 묻지 않았으며 컴파일 타임에 확인되는 유형 시스템으로 범위를 확인하기를 원했습니다.
— 지미 호파

1
@JimmyHoffa 아 이해가 되네요. 좋은 지적 :)
— Reactgular

2

범위는 변하지 않는 특별한 경우입니다. Wikipedia에서 :

안 불변는 프로그램의 실행 중에 사실이 의존 할 수있는 조건이다.

범위 [a, b]는 불변량 x> = a 및 x <= b를 가진 유형 의 변수 x 로 선언 될 수 있습니다 .Integer

따라서 Ada 또는 Pascal 하위 범위 유형이 반드시 필요한 것은 아닙니다. 그들은 변하지 않는 정수 타입으로 구현 될 수 있습니다.


0

이 기능이 언어에 추가되지 않은 것은 이상합니다.

C ++ 및 강력한 유형 시스템을 사용하는 다른 언어에서는 범위 제한 유형의 특수 기능이 필요하지 않습니다.

C ++에서 목표는 사용자 정의 유형 으로 비교적 간단하게 충족 될 수 있습니다 . 범위 제한 유형이 바람직한 응용 분야에서는 그다지 충분하지 않습니다 . 예를 들어, 속도 / 시간이 가속을 생성하고 가속 / 시간의 제곱근을 취하면 속도가 생성되도록 물리적 단위 계산이 올바르게 작성되었는지 컴파일러가 확인하기를 원할 것입니다. 이 작업을 편리하게 수행하려면 수식에 나타날 수있는 모든 유형을 명시 적으로 명명하지 않고 유형 시스템을 정의 할 수 있어야합니다. 이것은 C ++에서 수행 할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.