타입 시스템의 안전 이점은 무엇입니까?


47

에서 자바 스크립트 : 좋은 부품 더글러스 크록 포드에 의해, 그는 자신의 상속 장에서 언급

클래식 상속의 또 다른 이점은 형식 시스템의 사양을 포함한다는 것입니다. 이것은 대부분 프로그래머가 명시적인 캐스팅 작업을 작성하지 않아도되는데, 캐스팅 할 때 타입 시스템의 안전 이점이 없어지기 때문에 매우 좋습니다.

우선, 실제로 안전이란 무엇입니까? 데이터 손상, 해커 또는 시스템 오작동 등으로부터의 보호?

타입 시스템의 안전 이점은 무엇입니까? 이러한 안전 혜택을 제공 할 수있는 유형 시스템이 다른 이유는 무엇입니까?


타입 시스템이 컴파일되지 않은 언어에 어떤 이점을 제공한다고 확신하지는 않지만 컴파일 된 언어를 장기간 사용하는 사용자는 세심한 유형 검사를 통해 컴파일 된 언어가 모호하거나 정의되지 않았거나 불완전한 코드를 예방하는 데 효과적이라는 것을 알았습니다. "컴파일"단계를 지났다. 타입 힌트와 Lint 시스템이 웹 스크립팅 (JavaScript)에 유용하다고 말할 수있을 것입니다. 그렇다면 충분할 것입니다. 다트 사람? 정적 타입 시스템이 없어도 파이썬과 같은 동적 언어는 더 나빠 보이지 않습니다.
Warren P

1
오늘날 우리는 타이핑이 행동이 아니라 구조적 이어야 함을 이해합니다 . 안타깝게도 대부분의 현대 프로그래밍 언어는 유형의 동작을 주장 할 수있는 방법이 없습니다 ( 이 질문 을 잘 읽어보십시오). 이것은 대부분의 경우 타입 시스템을 매우 쓸모 없게 만듭니다. 여기에 언급 된 간단한 타입 에러는 일반적인 문제를 확인하는 영리한 린터에 의해 잡힐 수 있기 때문입니다.
Benjamin Gruenbaum

4
@BenjaminGruenbaum 당신의 설명은 이미 OCaml과 같은 언어로 정적으로 존재합니다. 구조적 타이핑이라고하며 실제로는 오래되고 공칭 타이핑은 최신입니다.
jozefg

2
@BenjaminGruenbaum : ... 무엇!? 정적으로 유형이 지정된 언어에서는 결정 불가능한 것이 거나 그러한 언어에 대한 컴파일러 작성은 불가능합니다.
BlueRaja-대니 Pflughoeft

6
@BenjaminGruenbaum : 귀하의 의견은 가치, 그리고 그것이 것을 증명하기 때문에, 그 논문은 재미있다, 그러나 "너무 자바와 같은 정적 언어에서 일반적으로 결정 불가능이다"고 청구를 부담하지 않는 것입니다 C #에서 decidable, 잎이 질문을 엽니 다 Java에서 결정할 수 없는지 여부 (어쨌든 IME는 정적으로 형식이 지정된 언어의 컴파일러가 형식이 올바른 것으로 판단 할 수없는 경우이를 거부 (또는 컴파일하지 못함)하므로 결정 불가능한 점은 형식에 구멍이 아니라 안전).
ruakh

답변:


82

타입 시스템으로 오류 방지

타입 시스템은 불법 프로그램을 제거합니다. 다음 파이썬 코드를 고려하십시오.

 a = 'foo'
 b = True
 c = a / b

파이썬에서이 프로그램은 실패합니다. 예외가 발생합니다. Java, C #, Haskell 과 같은 언어 에서 이것은 법적 프로그램이 아닙니다. 입력 프로그램 세트에서는 불가능하기 때문에 이러한 오류를 완전히 피하십시오.

마찬가지로 더 나은 유형의 시스템은 더 많은 오류를 배제합니다. 우리가 최고급 타입 시스템으로 뛰어 올라가면 다음과 같이 말할 수 있습니다.

 Definition divide x (y : {x : integer | x /= 0}) = x / y

이제 타입 시스템은 0으로 나누기 오류가 없음을 보장합니다.

어떤 종류의 오류

다음은 유형 시스템이 예방할 수있는 오류 목록입니다.

  1. 범위를 벗어난 오류
  2. SQL 주입
  3. 2, 많은 안전 문제 일반화 ( Perl 에서 오염 검사 가 무엇인지 )
  4. 순서에 맞지 않는 오류 (init 호출을 잊어 버림)
  5. 값의 하위 집합을 강제로 사용 (예 : 0보다 큰 정수만)
  6. 사악한 새끼 고양이 (예, 농담이었습니다)
  7. 정밀도 손실 오류
  8. 소프트웨어 트랜잭션 메모리 (STM) 오류 (순도, 유형도 필요)
  9. 8 일반화, 부작용 통제
  10. 데이터 구조에 대한 불변량 (이진 트리는 균형이 맞습니까?)
  11. 예외를 잊거나 잘못된 것을 던지기

그리고 이것은 또한 컴파일 타임에 있다는 것을 기억하십시오 . 단순히 타입 오류를 확인하기 위해 100 % 코드 적용 범위로 테스트를 작성할 필요가 없습니다.

사례 연구 : 유형 람다 미적분학

좋아, 모든 유형 시스템 중 가장 간단한 유형, 간단히 람다 미적분법을 살펴 봅시다 .

기본적으로 두 가지 유형이 있습니다.

Type = Unit | Type -> Type

그리고 모든 용어는 변수, 람다 또는 응용 프로그램입니다. 이를 바탕으로, 잘 입력 된 프로그램이 종료되었음을 증명할 수 있습니다. 프로그램이 멈추거나 반복되는 상황은 결코 없습니다. 이것은 정상적인 람다 미적분학에서는 증명할 수 없지만 사실이 아닙니다.

이것에 대해 생각해보십시오. 유형 시스템을 사용하여 프로그램이 영원히 반복되지 않고 오히려 멋지다는 것을 보장 할 수 있습니까?

동적 유형으로 우회

동적 유형 시스템은 정적 유형 시스템과 동일한 보장을 제공하지만 컴파일 타임이 아닌 런타임에 보장 할 수 있습니다. 실제로 런타임이기 때문에 실제로 더 많은 정보를 제공 할 수 있습니다. 그러나 특히 종료와 같은 정적 속성에 대한 일부 보장은 손실됩니다.

따라서 동적 유형은 특정 프로그램을 배제하지 않고 잘못된 형식의 프로그램을 예외 처리와 같은 잘 정의 된 조치로 라우팅합니다.

TLDR

따라서 길고 짧은 유형 시스템은 특정 프로그램을 배제한다는 것입니다. 많은 프로그램이 어떤 식 으로든 깨져 있으므로 유형 시스템을 사용하면 이러한 깨진 프로그램을 피할 수 있습니다.


25
많은 테스트를 작성하는 것과 동등하게 컴파일하기 위해 +1.
Dan Neely

3
@DanNeely 동적 언어에서는 타입 시스템이 무료로 확인하는 오류를 포착하기 위해 코드의 모든 부분을 연습해야한다는 것을 설명하기 위해서입니다. 그리고 의존적으로 유형이 지정된 언어에서는 실제로 테스트를 유형으로 완전히 대체 할 수 있습니다. 여러 번 당신은 비록 정확성의 추가 정리를 증명해야
jozefg

3
타입 시스템이 프로그램을 종료해야한다는 것을 증명했다면, 아마도 원시 재귀 함수를 계산하고 있음을 증명함으로써 그렇게 될 것입니다. 내가 생각하는 것은 시원하지만 진정한 Turing Machine이 해결할 수있는 것보다 훨씬 덜 흥미로운 복잡도 클래스입니다. (중간 값이 크지 않다는 것을 의미하지는 않습니다; Ackermann 함수는 원시 재귀…)
Donal Fellows

5
@DonalFellows Ackermann 함수는 전체 계산 함수이지만 원시 재귀는 아닙니다.
Taymon

4
@sacundim 정확히 agda와 같은 언어는 선택적 전체 검사를 허용하며 드문 경우에 임의의 재귀를 원할 경우 멋지게 요청할 수 있습니다. 매끄러운 시스템입니다.
jozefg

17

현실 자체가 입력됩니다. 가중치에는 길이를 추가 할 수 없습니다. 그리고 피트를 미터에 추가 할 수 있지만 (둘 다 길이의 단위 임) 둘 중 하나 이상을 확장해야합니다. 그렇게하지 않으면 화성 임무가 문자 그대로 중단 될 수 있습니다.

유형 안전 시스템에서 다른 단위로 표현 된 두 개의 길이를 추가하면 오류가 발생했거나 자동 캐스트가 발생했을 수 있습니다.


15

타입 시스템은 간단한 코딩 에러를 피하거나 컴파일러가 에러를 잡을 수 있도록 도와줍니다.

예를 들어, JavaScript 및 Python에서 다음 문제는 런타임시에만 발생하는 경우가 많으며 테스트 품질 / 조건의 희소성에 따라 실제로 프로덕션에 영향을 줄 수 있습니다.

if (someRareCondition)
     a = 1
else
     a = {1, 2, 3}

// 10 lines below
k = a.length

강력한 형식의 언어는 명시 적으로 a배열 임을 명시 하고 정수 할당을 허용하지 않습니다. 이런 식으로, 가장 드문 경우에도 기회 a가 없습니다 length.


5
WebStorm JavaScript와 같은 IDE의 영리한 린 터는 "a에 대한 a.length에 대한 정의되지 않은 참조 가능"이라고 말할 수 있습니다. 이것은 명시 적 타입 시스템을 통해 우리에게 주어지지 않습니다.
Benjamin Gruenbaum

4
1. 정적으로 강하지 않다 2. @BenjaminGruenbaum 예, 그러나 이것은 백그라운드에서 과제의 그래프를 쫓아서 이루어집니다. 사물이 어디로 가고 있는지 알아 내려고하는 미니 통역사처럼 생각하십시오. 종류는 무료로 당신에게 그것을 줄 때보다 훨씬 더 열심히
jozefg

6
@ BenjaminGruenbaum : 암묵적 / 명시 적을 강 / 약점과 혼동하지 마십시오. 예를 들어, Haskell은 대부분의 다른 언어를 부끄러워하는 엄청나게 강력한 유형 시스템을 가지고 있지만 특정 언어 디자인 결정으로 인해 거의 보편적으로 유형 유추가 가능하여 명시 적 타이핑을 지원하는 강력한 암시 적 유형 언어가됩니다. (유형 추론자는 의도 한 것이 아니라 쓴 내용 만 추론 할 수 있기 때문에 사용해야합니다 !)
Phoshi

6
"강력한 유형의 언어를 사용하면 a가 배열임을 명시 적으로 명시하게됩니다."잘못되었습니다. 파이썬은 강력하게 타이핑되어 있으므로 필요하지 않습니다. 정적이고 강력하게 유형이 지정된 언어조차도 유형 유추를 지원하는 경우 (그리고 오늘날 대부분의 주류 언어는 적어도 부분적으로는) 요구하지 않습니다.
Konrad Rudolph

1
@ BenjaminGruenbaum : 아, 충분합니다. 그럼에도 불구하고 JS 정적 분석기가 강력한 유형의 언어가 제공하는 것과 동일한 종류의 유형 검사를 수행 할 수없는 경우가 있으며, 일반적인 경우에는 중지 문제를 해결해야합니다. Haskell은 거의 100 %에 가까운 형식 유추를 달성하기 위해 공정한 몇 가지 설계 결정을 내려야했으며 C # / Scala는 모든 것을 유추 할 수는 없습니다. 물론 이러한 경우에는 명시 적으로 유형을 지정할 수 있기 때문에 중요하지 않습니다. Javascript에서는 최고의 정적 분석기조차 더 이상 코드를 확인할 수 없습니다.
Phoshi

5

소프트웨어 개발주기가 빠를수록 오류를 잡을 수 있으며 비용이 적게 듭니다. 가장 큰 클라이언트 또는 모든 클라이언트가 데이터를 유실하는 오류를 고려하십시오. 이러한 오류는 실제 고객이 데이터를 잃은 후에 만 ​​잡히면 회사의 종말이 될 수 있습니다! 이 버그를 찾아서 프로덕션으로 옮기기 전에 수정하는 것이 분명히 저렴합니다 .

적은 비용으로 오류가 발생하더라도 프로그래머가 오류를 찾아서 고칠 수있는 것보다 테스터가 관여하면 더 많은 시간과 에너지가 소비됩니다. 다른 프로그래머가 소스 제어에 의존하는 소프트웨어를 빌드 할 수있는 소스 제어에 체크인하지 않으면 더 저렴합니다. 형식 안전성은 특정 클래스의 오류가 컴파일되는 것을 방지하여 이러한 오류의 거의 모든 잠재적 비용을 제거합니다.

그러나 그것은 전체 이야기가 아닙니다. 동적 인 언어로 프로그램을하는 사람이라면 누구나 알겠지만, 가끔은 프로그램이 컴파일되는 것이 좋기 때문에 모든 세부 사항을 해결하지 않고도 프로그램의 일부를 시험해 볼 수 있습니다. 안전과 편의 사이에는 절충이 있습니다. 단위 테스트는 동적 언어를 사용하는 위험을 완화 할 수 있지만 좋은 단위 테스트를 작성하고 유지하는 데 비용이 많이 들기 때문에 형식에 안전한 언어를 사용하는 것보다 비용이 많이 듭니다.

실험 중이라면 코드를 한 번만 사용하는 경우 (예 : 일회성 보고서) 단위 테스트를 작성하지 않아도되는 상황에서는 동적 언어가 완벽 할 수 있습니다. 당신을 위해. 큰 응용 프로그램을 사용하고 나머지 부분을 중단하지 않고 한 부분을 변경하려면 유형 안전이 생명을 구합니다. 유형 안전 잡기 유형은 리팩토링시 사람이 간과하거나 잘못하는 경향이있는 유형입니다.


이것은 동적 타이핑을 짧게 판매하여 주요 이점을 언급하지 못합니다 (언급 된 이점은 상대적으로 중요하지 않으므로 편리합니다). 또한 단위 테스트에 대해 이상한 것을 암시하는 것 같습니다. 예, 어렵고 비용이 많이 들며 정적으로 유형이 지정된 언어에도 적용됩니다. 이 말은 무엇입니까? 또한 표현할 수있는 것과 오류를 잡을 수있는 현재 유형 시스템의 한계 (설계 상)에 대해서는 언급하지 않습니다.

@MattFenwick 동적 타이핑의 주요 이점은 무엇이라고 생각하십니까?
GlenPeterson

일반적인 정적 유형 시스템은 설계에 따라 많은 유형이 지정된 프로그램을 거부합니다. ( 대안 ) (BTW, 나의 비판은 세 번째 및 네 번째 단락에만 해당되었습니다.)

4

소개

정적 형식 (컴파일 된 정적 형식 검사) 및 / 또는 런타임 (평가 된 동적 형식 검사) 언어를 사용하여 형식 안전을 달성 할 수 있습니다. Wikipedia 에 따르면 '... 강력한 유형 시스템 은 확인되지 않은 런타임 유형 오류 (Luca Cardelli)가 발생할 가능성이없는 시스템 으로 설명됩니다. 다른 말로, 확인되지 않은 런타임 오류가없는 것을 안전 또는 유형 안전이라고합니다. '

안전-정적 유형 확인

기본적으로 형식 안전성은 C, C ++ 및 Haskell과 같은 언어에서 컴파일시 형식 불일치를 감지하도록 설계된 정적 형식과 동의어입니다. 이것은 프로그램이 실행될 때 잠재적으로 정의되지 않거나 오류가 발생하기 쉬운 조건을 피할 수 있다는 이점이 있습니다. 예를 들어, 감지되지 않으면 치명적인 결과를 초래할 수있는 상황과 같이 포인터 유형이 일치하지 않을 위험이있는 경우 이는 매우 중요합니다. 이런 의미에서 정적 타이핑은 메모리 안전과 동의어로 간주됩니다.

그러나 정적 타이핑은 완전히 안전하지는 않지만 안전성을 향상시킵니다 . 정적으로 유형이 지정된 시스템조차도 치명적인 결과를 초래할 수 있습니다. 많은 전문가들은 정적 유형을 사용하면보다 강력하고 오류가 발생하기 쉬운 (미션 크리티컬) 시스템을 작성할 수 있다고 생각합니다.

정적으로 유형이 지정된 언어 숫자 작업에서 데이터 손실 또는 정확도 손실의 위험을 줄이는 데 도움이 될 수 있는데, 이는 불일치 또는 더블 투 플로트 또는 불일치 정수 및 플로트 유형의 잘림으로 인해 발생할 수 있습니다.

정적 인 유형의 언어를 사용하면 효율성과 실행 속도가 향상되는 이점이 있습니다. 런타임은 실행 중에 유형을 결정할 필요가 없다는 이점이 있습니다.

안전-런타임 유형 확인

예를 들어, Erlang은 가상 시스템에서 실행되는 형식 선언적이고 동적으로 유형을 검사하는 언어입니다. Erlang 코드는 바이트 컴파일 될 수 있습니다. Erlang은 아마도 가장 중요한 미션 크리티컬, 내결함성 언어로 간주되며 Erlang의 신뢰도는 9 9 (99.9999999 % 또는 31.5 msecs / 년)입니다.

Common Lisp와 같은 특정 언어는 정적으로 유형이 지정되지 않지만 원하는 경우 속도와 효율성을 향상시키는 데 도움이 될 수있는 유형을 선언 할 수 있습니다. 또한 파이썬과 같이 널리 사용되는 많은 해석 언어는 평가 루프 아래에 C 또는 C ++와 같이 정적으로 유형이 지정된 언어로 작성됩니다. Commom Lisp와 Python은 위의 정의에 따라 유형 안전으로 간주됩니다.


2
나는 "강력한 유형"에 반대한다. 정적으로 입력 한 것을 의미합니다. 강력하게 유형은 거의 의미가 없습니다. 기본적으로 "이 유형 시스템을 좋아합니다"
jozefg

@ jozefg 좋은 지적입니다. 게시물을 수정하겠습니다.
AsymLabs

3
또한 언어 구현에 대해 해석되는 언어를 말하는 것이 유용하지는 않지만 언어 자체는 아닙니다. 모든 언어를 해석하거나 컴파일 할 수 있습니다. 그리고 편집 후에도 강하고 약한 타이핑이라는 용어를 사용하고 있습니다.
Esailija

3
@jozefg : 항상 강력한 형식은 각 값에 고정 된 형식 (예 : 정수, 문자열 등)이 있고 약한 형식은 값이 다른 유형의 값으로 강제 변환 될 수 있음을 의미한다고 생각했습니다. 그래서. 예를 들어, 파이썬 (강하게 입력 된)에서는 1 + "1"예외가 발생하는 반면, PHP (약하게 입력 된) 1 + "1"에서는 생성됩니다 2(문자열 "1"은 자동으로 integer로 변환 됨 1).
Giorgio

1
Java와 같은 이러한 정의를 가진 @Giorgio는 강력하게 입력되지 않습니다. 그러나 많은 경우에 주장하고 있습니다. 이 단어에는 의미가 없습니다. 강력하고 약한 유형은 jozefg와 같이 "이 언어를 좋아합니다 /하지 않습니다"로 훨씬 정확하게 정의됩니다.
Esailija

1

유형 시스템의 안전 이점이 손실됩니다.

우선, 실제로 안전이란 무엇입니까? 데이터 손상, 해커 또는 시스템 오작동 등으로부터의 보호?

타입 시스템의 안전 이점은 무엇입니까? 이러한 안전 혜택을 제공 할 수있는 유형 시스템이 다른 이유는 무엇입니까?

타입 시스템이 그런 부정적인 견해를 갖고있는 것 같습니다. 타입 시스템은 오류가 없다는 것을 보증하는 것보다 보증하는 것이 아닙니다. 후자는 형식 시스템의 결과입니다. 프로그래밍 언어의 형식 시스템은 컴파일 타임에 프로그램이 특정 종류의 사양을 충족한다는 증거를 생성하는 방법입니다.

유형으로 인코딩 할 수있는 사양의 종류는 언어 유형 체계의 강도에 따라 언어에 따라 결정됩니다.

가장 기본적인 종류의 사양은 함수의 입출력 동작과 함수 본체 내부의 유효성에 대한 보증입니다. 함수 헤더를 고려하십시오

f : (Int,Int) -> String

좋은 타입 시스템은 f가 평가할 때 한 쌍의 Int를 생성하는 객체에만 적용되도록하고 f가 항상 문자열을 생성하도록 보장합니다.

if-then 블록과 같은 언어의 일부 문장에는 입력 / 출력 동작이 없습니다. 여기서 타입 시스템은 블록의 각 선언이나 문장이 유효하다는 것을 보증합니다. 즉, 올바른 종류의 개체에 작업을 적용합니다. 이러한 보증은 작성 가능합니다.

또한 이것은 일종의 메모리 안전 조건을 제공합니다. 당신이 다루고있는 인용은 캐스팅에 관한 것입니다. 경우에 따라 32 비트 Int를 64 비트 Int로 캐스팅하는 것과 같이 캐스팅에 문제가 없습니다. 그러나 일반적으로 유형 시스템이 충돌합니다.

치다

Foo x = new Foo(3,4,5,6);
f((Int)x,(Int)x);

캐스팅으로 인해 x는 Int로 바뀌므로 기술적으로 위의 유형 검사가 수행됩니다. 그러나 실제로 유형 검사의 목적을 무효화합니다.

더 좋고 다른 유형의 시스템을 만들 수있는 한 가지 방법은 B가 A의 하위 유형 (또는 하위 객체)이 아닌 한, 캐스트 (A) x를 사례 앞의 x가 B 유형 인 경우를 배제하는 것입니다. 정수 오버플로 / 언더 플로 공격의 가능성을 제거합니다.

요약

타입 시스템은 프로그램이 어떤 종류의 사양을 충족하는지 증명하는 방법입니다. 유형 시스템이 제공 할 수있는 이점은 사용 된 유형 시스템의 강도에 따라 다릅니다.


1

유형 시스템에 대해 아직 언급되지 않은 한 가지 장점은 많은 프로그램이 작성된 것보다 더 많이 읽히는 사실을 중심으로하며, 많은 경우 유형 시스템은 간결하고 쉽게 할 수있는 방식으로 많은 정보를 지정할 수 있습니다. 코드를 읽는 사람이 소화합니다. 매개 변수 유형은 설명 주석을 대신하지 않지만 대부분의 사람들은 "int Distance;"를 더 빨리 읽을 수 있습니다. 또는Distance As Int32"거리는 정수 +/- 2147483647이어야합니다"를 읽는 것보다; 또한 분수를 통과하면 결과가 일치하지 않을 수 있습니다. "또한 매개 변수 유형은 특정 API 구현과 수행자가 호출 할 수있는 권한 간의 차이를 줄이는 데 도움이됩니다. 예를 들어 API의 특정 Javascript 구현에서 숫자 형식에 모든 문자열을 강제 할 수있는 방법의 매개 변수, 호출자가 그런 행동에 의존 할 수, 또는 여부 불분명 할 수있는 API의 다른 구현이 제대로 작동 할 수있는 경우 경우 주어진 문자열. 누구의 매개 변수로 지정하는 방법 갖는 Double것을 받아들이는 오버로드와 방법를 갖는 임의의 문자열 값이 전달되기 전에 호출에 의해 강제되어야한다는 것이 명확하게 Double그 허용하고 다른String 문자열을 보유한 발신자가 문자열을 전달할 수 있음을 다소 명확하게합니다.


0

우선, 실제로 안전이란 무엇입니까? 데이터 손상, 해커 또는 시스템 오작동 등으로부터의 보호?

다른 모든 답변과 더. 일반적으로 "유형 안전"은 컴파일러가 성공적으로 컴파일하는 프로그램 중 어느 것도 유형 오류를 포함하지 않음을 의미합니다.

자, 타입 에러는 무엇입니까? 원칙적으로 원하지 않는 속성을 유형 오류로 지정할 수 있으며 일부 유형 시스템은 프로그램에 이러한 오류가 없는지 정적으로 확인할 수 있습니다.

위의 "속성"이란 프로그램에 적용되는 논리 제안 유형을 의미합니다 (예 : "모든 인덱스가 배열 범위 내에 있음"). 다른 유형의 속성에는 "지연된 모든 포인터가 유효합니다", "이 프로그램은 I / O를 수행하지 않습니다"또는 "이 프로그램은 / dev / null에 대해서만 I / O를 수행합니다"등이 있습니다. 유형 시스템의 표현 방식에 따라 이러한 방식으로 속성을 지정하고 유형을 확인할 수 있습니다.

종속 유형 시스템은 가장 일반적인 유형 시스템 중 하나이며,이를 통해 원하는 거의 모든 속성을 적용 할 수 있습니다. 정교한 속성이 불완전 성으로 인해 Gödel에 의해 제공 되기 때문에 반드시 그렇게 쉬운 것은 아닙니다 .

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