평활화된 구간 전환
요구사항
온도 센서가 하루에 원시 판독값 12개를 보고합니다. 이를
3-판독 이동 평균으로 평활화하고, 평활화된 값을
구간으로 분류한 뒤(cool < 20° ≤ ok <
25° ≤ hot), 모든 구간 전환을 보고하세요 —
어느 구간에서 나와 어느 구간으로 들어갔는지, 그리고 양쪽의 평활값을
함께 출력합니다. 전환이 없는 날은 아무것도 출력하지 않는 대신
stable 한 줄을 출력합니다. 7월 이틀치 데이터는 코드에
있으며, 두 버전 모두 예상 출력 아래에 표시된 줄들을
출력해야 합니다.
예상 출력
2026-07-14 (3-reading moving average): cool → ok (avg 18.6 → 21.2) ok → hot (avg 21.2 → 25.6) hot → ok (avg 25.6 → 24.9) 2026-07-15 (3-reading moving average): stable — no zone changes
나란히 보기
네이티브 Dart
FxDart
차이가 나는 이유
이 과제의 모든 단계는 이웃한 요소를 봐야 하는데, 바로
거기서 순수 Dart의 어휘가 바닥납니다: 슬라이딩 윈도우도, 인접 중복
제거도, 후속 값 짝짓기도 표준 라이브러리와
package:collection 어디에도 없습니다
(slices는 겹침 없이 타일링만 합니다). 그래서 네이티브
버전은 각자 가변 상태를 든 인덱스 루프 세 개가 됩니다: 윈도우 합계,
자기 꼬리와 비교하는 runStarts 리스트,
전환 줄을 위한 i - 1 되돌아보기 — 거기에
stable 날을 위한 마지막 isEmpty 땜질까지.
FxDart 체인은 다섯 단계를 데이터가 흐르는 순서대로 서술합니다:
windowed(3) → 윈도우별 average,
uniqAdjacentBy(zone)가 각 구간 런의 첫 평활값만 남기고,
pairwise가 런 시작점들을 (from, to) 전환으로
바꾸고, ifEmpty가 stable 날의 줄을 파이프라인 바깥의
if 검사 대신 파이프라인 안에서 공급합니다. 각 조각은 독립적으로
테스트할 수 있고, 윈도우 경계를 다시 구현하는 코드는 어디에도
없습니다. 이 네 연산자는 Rx 윈도잉 계열의 풀(pull) 모델
이식입니다.
벤치마크
N = 100
시간 무승부
최대 메모리 무승부
N = 10,000
시간 무승부
최대 메모리 무승부
N = 1,000,000
시간 FxDart 승
최대 메모리 FxDart 승
막대는 사이드별로 새 프로세스에서 반복 측정한 중앙값입니다(작은 N은 타이머 해상도를 위해 배치 처리). 두 사이드가 서로 5% 이내이거나 — 사람이 지각할 수 없는 차이인 0.6ms 이내이면 — 무승부로 칩니다. 상대 차이가 근소한 경우는 최대 5회까지 다시 측정합니다. 앱에서는 어느 막대가 짧든 몇 밀리초 이하의 차이는 사용자에게 보이지 않습니다. 메모리는 프로세스 최대 RSS입니다. Dart VM과 데이터셋은 양쪽이 동일하므로, 두 막대의 차이가 곧 파이프라인 자체가 붙들고 있는 양입니다.