본문으로 건너뛰기

가변 길이 레코드 배열

문서 목록으로

레코드 배열의 길이를 로우마다 다르게 하는 기능의 설계입니다. 중첩 필드를 먼저 읽어야 합니다 — 그 위에 얹는 것입니다.


1. 문제

레코드 그룹의 배열 길이는 컬럼 수입니다. Slot1.*·Slot2.*·Slot3.*이면 모든 로우가 길이 3입니다. 값을 두 개만 적은 로우도 세 번째 원소를 갖고, 그 원소는 타입의 빈 값으로 찹니다.

읽는 쪽에서는 그 원소가 적은 것인지 안 적은 것인지 알 수 없습니다. {Id:0, Count:0}이 「0개를 주는 슬롯」인지 「슬롯이 없음」인지 구별되지 않습니다. 그래서 소비하는 코드마다 「전부 0이면 없는 것으로 친다」를 손으로 적게 되고, 그 규칙은 어디에도 적혀 있지 않습니다.

그 레이아웃은 이것을 익스포터가 처리합니다 — 값이 있는 마지막 원소까지로 배열을 자릅니다. 두 워크북의 대조에서 우리 쪽 격차로 남은 것이 이것뿐입니다 (material 94행, language 1,376행).

2. 결정 — 뒤에서 절단

값이 없는 뒤쪽 원소를 버립니다. 가운데는 건드리지 않습니다.

로우Slot1Slot2Slot3나오는 길이
13
2없음2
3없음3 — 가운데는 그대로
4없음없음없음0

가운데를 지우지 않는 이유. 지우면 원소의 위치가 바뀝니다. Slot2에 적은 것이 로우마다 [0]이거나 [1]이 되고, 그러면 컬럼과 인덱스의 대응이 로우마다 다릅니다 — 컬럼에 이름을 붙여 적은 시트에서 그것을 뜻했을 리가 없습니다. 뒤에서만 자르면 인덱스 k는 언제나 Slot{k+1} 입니다.

「값이 없다」의 정의

레이아웃이 정합니다. 코어는 셀마다 Cell.HasValue 하나만 읽습니다.

레이아웃값이 없는 셀
tabbit타입에 ?가 붙은 컬럼의 - (빈 칸과 없음)
named-range-, 그리고 빈 칸

파싱된 값으로 판정하지 않습니다. 0을 적은 셀과 값이 없다고 적은 셀은 둘 다 0이 되므로, 값을 보면 작성자가 적은 0을 지우게 됩니다. 「비었다」는 셀의 성질이고 값의 성질이 아닙니다.

빈 칸으로도 판정하지 않습니다. 빈 칸은 그 타입이 읽는 값이고 — string?이면 빈 문자열입니다 — 빈 이름을 적은 원소를 지우는 것은 작성자가 적은 0을 지우는 것과 같은 일입니다.

원소는 멤버 전부가 값이 없을 때 비어 있습니다. 하나라도 있으면 그 원소는 남고, 값이 없는 멤버는 빈 값으로 나갑니다. 멤버마다 따로 자르면 한 원소의 멤버들이 서로 다른 인덱스에 놓입니다.

옵트인 설정

기본은 입니다. 켜면 배열이 짧아지고, 그것은 조용합니다 — serial field가 옵트인이 된 것과 같은 이유입니다. 소스 항목의 TrimTrailingArrayElements로 켭니다. 스칼라 배열도 같은 규칙으로 잘립니다 — 자르기는 「원소가 어디서 끝나는가」에 대한 답이고, 그 질문에 원소의 생김새는 상관이 없습니다. 배열 컬럼들이 필수 여부를 서로 다르게 적으면 첫 원소의 답을 씁니다.

다른 레이아웃은 이 설정을 읽지 않고 항상 자릅니다. 그 시트들의 규칙이 그것이고, 그 규칙은 그 레이아웃 파일 안에 있습니다.

3. 가운데 빈칸 — 기본은 거부

2절은 가운데를 유지한다고 정하였고, 그것은 여전히 맞습니다. 남아 있던 질문은 다른 것입니다 — 가운데가 빈 배열을 애초에 받을 것인가.

결정. 받지 않습니다. 가운데 빈칸은 어느 셀인지 함께 보고하고 변환을 멈춥니다. 받겠다고 적은 소스에서만 통과합니다.

로우Slot1Slot2Slot3기본AllowArrayGaps
133
2없음2 — 뒤에서 자름2
3없음거부3 — 가운데는 그대로
4없음없음없음00

근거 — 관대함이 통과시키는 것

가운데 빈칸은 거의 언제나 실수입니다. Slot2를 지우고 Slot3을 그대로 둔 것이 의도인 시트는 드물고, 의도였다면 그 자리에 뜻이 있는 값을 적었을 것입니다. 지금은 그것이 조용히 길이 3짜리 배열이 되어 나가고, 가운데 원소는 타입의 빈 값입니다 — 소비하는 쪽에서 「없음」과 「0」이 구별되지 않습니다.

이 도구의 역할이 정적 검증인데, 다른 익스포터가 검출하는 것을 통과시키고 있었습니다.

AllowArrayGaps — 받겠다고 적는 자리

소스 항목의 정식 프로퍼티입니다. TrimTrailingArrayElements와 같은 자리이고, 같은 이유로 그렇습니다 — 어느 레이아웃인지 몰라도 찾을 수 있어야 하는 질문이기 때문입니다. 배열을 가진 레이아웃이면 어느 것이든 이 질문을 받습니다.

false (기본)가운데 빈칸은 어느 셀인지 함께 보고하고 멈춥니다
true2절의 표대로 가운데를 유지하고 통과시킵니다

기본을 거부로 둔 것은 방향의 문제입니다. 관대한 기본은 실수를 데이터로 만들고, 엄격한 기본은 의도를 한 줄 적게 만듭니다. 뒤쪽이 되돌릴 수 있는 쪽입니다.

「가운데」의 범위

뒤에서 자른 뒤에 봅니다. 자르기가 켜져 있으면 마지막 값 뒤의 빈칸은 배열의 밖이므로 가운데가 아닙니다. 자르기가 꺼져 있으면 배열의 길이는 컬럼 수이고, 마지막 값 뒤의 빈칸도 가운데입니다 — 그 설정에서는 모든 자리가 배열 안이기 때문입니다.

「값이 없다」의 정의는 2절 그대로 레이아웃이 정합니다. 코어는 Cell.HasValue 하나만 읽습니다.

4. 와이어 — 형식 변경 없음

레코드의 멤버 하나가 KindVarArray 컬럼이 됩니다. 지금은 KindFixedArray입니다.

Slot.Id kind=varArray [로우0: 3, id,id,id][로우1: 2, id,id][로우2: 0]
Slot.Count kind=varArray [로우0: 3, c,c,c ][로우1: 2, c,c ][로우2: 0]

형식 변경도, 버전 올림도, 모든 리더 수정도 없습니다. KindVarArray는 v100부터 있고 모든 리더가 읽습니다 — 구분자로 구분된 int[] 셀이 그것을 씁니다. 새 kind 값도 쓰지 않습니다 (남은 한 자리는 그대로 비어 있습니다).

길이가 멤버마다 반복됩니다. 멤버 M개면 로우당 counter32가 M개입니다. 대신 다음을 사지 않았습니다.

  • 새 kind나 「길이를 공유하는 컬럼」 개념 — 그러면 모든 리더가 컬럼 사이의 의존을 알아야 합니다
  • 컬럼을 혼자 읽을 수 없게 되는 것 — 지금은 어느 컬럼이든 자기 블록만으로 해석됩니다. 모르는 컬럼을 바이트 길이로 건너뛰는 것이 스키마 진화의 근거인데, 길이가 다른 컬럼에 있으면 그것이 깨집니다

길이가 로우마다 크게 다르지 않으면 인코딩이 대부분 가져갑니다. 값 자체가 줄어드는 것이 (빈 원소를 안 쓰므로) 보통 더 큽니다.

생성된 코드는 멤버마다 길이를 읽고, 서로 다르면 거부합니다. 쓰는 쪽이 같게 쓰므로 다르면 파일이 깨진 것이고, 조용히 짧은 쪽을 택하면 그 뒤의 값들이 한 칸씩 밀립니다.

5. 스칼라 배열 — 미지원

Text1/Text2/Text3도 같은 이유로 자를 수 있고, 스칼라 배열이 그것을 원합니다. 여기 넣지 않은 이유는 순서뿐입니다 — 레코드가 대조에서 나온 격차이고, 스칼라 serial field는 이미 있는 기능이라 자르면 출력이 바뀌는 사람이 있습니다. 레코드는 새 기능이라 바뀔 출력이 없습니다.

같은 설정 이름으로 넓히면 됩니다. WireColumn 쪽은 이미 같은 코드를 지납니다.