Devin.KR

값·조건·반복으로 센서 읽기

90분 안팎

학습 목표

거리 목록에서 음수와 범위 밖 값을 분리하고 유효 개수를 출력합니다.

개념

왜 거리 값부터 검사하는가

자율 이동 로봇은 앞에 무엇이 있는지 거리 센서로 관찰합니다. 하지만 숫자가 들어왔다는 사실만으로 이동 판단에 사용할 수는 없습니다. 센서 연결이 끊겨 음수 표식이 들어오거나 측정 가능 범위를 넘은 값이 섞이면, 평균이나 최솟값을 계산하기 전에 입력을 분리해야 합니다. 이 레슨에서는 로봇을 움직이지 않고 거리 목록의 유효 개수를 세어 입력 경계를 만듭니다. 판단을 눈으로 예상한 뒤 프로그램 결과와 비교하는 것이 첫 번째 작업입니다.

이번 모듈은 Python 3와 bash, 텍스트 편집기를 사용합니다. 외부 패키지를 설치하지 않으며 브라우저 실습은 표준 입력을 읽는 Python 프로그램입니다. 로컬 실습은 시작 코드 ZIP을 풀고 README에 적힌 명령으로 확인합니다. 실물 장치나 ROS 2 없이 PC에서 기록을 처리합니다. 이 결과가 실제 장치의 안전성을 보장하지는 않으며, 이후 좌표 변환과 통신 계약을 얹을 실행 기반을 준비하는 단계입니다.

값과 이름을 구분하기

변수는 값에 붙이는 이름입니다. distance_m = 1.2에서 오른쪽은 미터 단위의 숫자이고 왼쪽은 그 값을 다음 계산에서 찾는 이름입니다. 대입에 쓰는 =와 두 값이 같은지 묻는 ==를 구분합니다. distance라는 이름만 쓰면 단위가 감춰지므로 distance_m처럼 단위를 드러냅니다. 숫자 1.2와 문자열 "1.2"는 다릅니다. 화면에 비슷하게 보여도 문자열은 바로 수치 비교에 사용할 수 없고 변환이 필요합니다.

정수는 개수와 순번을 표현하고 실수는 거리 같은 연속량을 표현합니다. valid_count에는 정수 0을, distance_m에는 실수 0.05를 넣는 식입니다. 소수 계산은 컴퓨터 내부 표현 때문에 정확한 십진 소수와 차이가 날 수 있습니다. 이번에는 입력 값을 경계와 직접 비교하며 합이나 평균은 아직 구하지 않습니다. 문자열을 float로 바꾸는 시점과 유효 범위를 판단하는 시점을 분리하면 변환 오류와 범위 오류를 혼동하지 않습니다.

유효 범위를 계약으로 쓰기

이 프로젝트의 거리 계약은 0.05m부터 8.0m까지이며 두 끝값을 포함합니다. 특정 센서 제품의 보편 사양이 아니라 실습을 위해 정한 범위입니다. 따라서 0.05와 8.0은 유효하고, 0.049와 8.001은 범위 밖입니다. 음수는 별도로 셉니다. 0은 음수는 아니지만 최소 거리 미만이므로 범위 밖에 들어갑니다. 유효·음수·그 밖의 범위 밖이라는 세 분류를 서로 겹치지 않게 정해야 개수의 합으로 누락을 찾을 수 있습니다.

if는 조건이 참일 때 들여쓴 블록을 실행합니다. elif는 앞 조건이 거짓일 때 다음 조건을 확인하고 else는 남은 경우를 처리합니다. 먼저 음수인지 확인하고, 이어서 0.05 <= value <= 8.0인지 확인하면 세 분류가 됩니다. Python의 연쇄 비교는 최소 조건과 최대 조건을 함께 만족해야 참입니다. value >= 0.05 or value <= 8.0처럼 쓰면 많은 잘못된 값도 한쪽 조건을 만족해 통과합니다. 자연어의 “범위 안”을 두 조건의 동시 만족으로 옮깁니다.

목록 전체를 한 번씩 처리하기

리스트는 여러 값을 순서대로 묶습니다. for value in values는 리스트에서 값을 하나씩 꺼내 value라는 이름으로 처리합니다. 반복을 시작하기 전에 세 카운터를 0으로 만들고 각 분기에서 해당 카운터에 1을 더합니다. 카운터를 반복문 안에서 0으로 만들면 마지막 값의 분류만 남습니다. 모든 값을 분류하는 작업에는 break를 넣지 않습니다. 한 음수를 만났다는 이유로 반복을 끝내면 뒤에 있는 정상 거리까지 읽지 못합니다.

들여쓰기는 단순한 모양이 아니라 블록의 범위를 정하는 문법입니다. 조건 아래의 증가문은 조건보다 네 칸 더 들여쓰고, 최종 출력은 for와 같은 위치에 둡니다. 출력까지 반복문 안에 넣으면 중간 집계가 값의 개수만큼 나옵니다. 탭과 공백을 섞지 않고 편집기를 공백 네 칸으로 설정합니다. IndentationError는 들여쓰기 구조가 잘못되었다는 뜻이며 숫자 값 자체가 틀렸다는 뜻은 아닙니다. 먼저 오류가 가리키는 줄과 그 위의 콜론을 확인합니다.

표준 입력과 빈 기록

브라우저 실습 입력은 공백으로 구분한 유한한 거리 숫자이며 줄바꿈도 공백으로 취급합니다. sys.stdin.read().split()으로 토큰을 얻고 float(token)으로 변환합니다. 빈 입력은 토큰이 없는 목록이므로 반복이 한 번도 실행되지 않고 세 개수가 모두 0입니다. 입력에 건수를 먼저 넣는 형식이 아니므로 첫 숫자도 거리입니다. NaN, 무한대, 숫자가 아닌 문자열은 이번 브라우저 입력 계약에서 제외하고 뒤의 파일 검증 실습에서 따로 다룹니다.

결과는 valid=N negative=N outside=N 형식으로 한 줄만 출력합니다. 문장이나 디버그 출력을 추가하면 사람이 이해해도 채점 계약과 달라집니다. print는 줄 끝에 줄바꿈을 붙입니다. 유효 개수는 정수이므로 3으로 출력하고 3.0으로 바꾸지 않습니다. 재현을 위해 입력 원본과 기대하는 세 개수를 함께 남깁니다. 빈 기록의 결과가 0이라는 사실을 정상 센서 상태로 확대 해석하지는 않습니다. 유효 데이터가 없다는 별도 상태 판단은 통신 단계에서 추가합니다.

틀린 결과를 좁히는 방법

먼저 정상 값 한 개로 반복과 출력을 확인하고, 두 경계값과 그 바로 바깥 값을 넣어 비교 기호를 점검합니다. 결과가 예상보다 크면 or를 썼는지, 작으면 끝값을 제외했는지 살펴봅니다. 합계가 입력 개수와 다르면 한 분기에서 두 번 증가했거나 값을 건너뛴 것입니다. 원본 목록에서 원소를 삭제하는 방식은 쓰지 않습니다. 삭제 때문에 순회 위치가 달라지면 연속된 잘못된 값 중 일부가 남을 수 있어 새 카운터로 관찰 결과만 모읍니다.

ValueError: could not convert string to float는 토큰을 숫자로 읽지 못했다는 뜻입니다. 이런 경우 범위를 늘려 해결하지 말고 입력에 문자나 쉼표가 있는지 확인합니다. TypeError가 비교식에서 나오면 숫자와 문자열이 섞였는지 점검합니다. 이번 실습을 마치면 거리 기록의 유효 개수뿐 아니라 음수와 범위 밖 개수를 서로 배타적으로 출력할 수 있어야 합니다. 반복과 조건문의 일반 문법은 더 읽기로 복습하고, 여기서는 센서 계약에 맞춘 분류 결과를 증거로 남깁니다.

따라하기

값과 단위 확인

숫자와 문자열의 형식, 거리 변수의 단위를 확인합니다.

distance_m = 0.05
text = "0.05"
print(type(distance_m).__name__, type(text).__name__)
print(distance_m == float(text))

실행 결과

float str
True

세 분류를 계산

분기마다 한 카운터만 증가시킵니다. 마지막 출력은 반복문 바깥에 둡니다.

values = [-1, 0, 0.05, 8.0, 8.01]
valid = negative = outside = 0
for value in values:
    if value < 0:
        negative += 1
    elif 0.05 <= value <= 8.0:
        valid += 1
    else:
        outside += 1
print(f"valid={valid} negative={negative} outside={outside}")
print("total", valid + negative + outside)

실행 결과

valid=2 negative=1 outside=2
total 5

끝값과 빈 목록 확인

경계 바로 바깥 값을 비교한 뒤, 빈 목록에서 집계가 0임을 확인합니다.

values = [0.049, 0.05, 8.0, 8.001]
for value in values:
    print(value, 0.05 <= value <= 8.0)
print("empty", sum(1 for value in [] if 0.05 <= value <= 8.0))

실행 결과

0.049 False
0.05 True
8.0 True
8.001 False
empty 0

확인 문제

실습

공백으로 구분한 유한한 m 거리 숫자를 읽어 유효(0.05~8.0 포함), 음수, 나머지 범위 밖 개수를 출력합니다. 건수 머리말은 없습니다. 빈 입력은 세 개수 모두 0입니다. 출력은 valid=N negative=N outside=N 한 줄이며 입력 원본은 수정하지 않습니다.

모범 답안
import sys
values = [float(token) for token in sys.stdin.read().split()]
valid = negative = outside = 0
for value in values:
    if value < 0:
        negative += 1
    elif 0.05 <= value <= 8.0:
        valid += 1
    else:
        outside += 1
print(f"valid={valid} negative={negative} outside={outside}")

더 읽기

면접 질문

  • 센서 입력의 숫자 범위와 입력 부재를 어떻게 구분합니까?