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


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> = ax <= b를 가진 유형 의 변수 x 로 선언 될 수 있습니다 .Integer

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


0

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

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

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

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