루비의 배열 슬라이싱: 비논리적인 행동에 대한 설명(Rubykoans.com 에서 제공)
저는 루비 코안스에서 연습을 하고 있었는데, 제가 정말 설명할 수 없는 루비 기괴함에 놀랐습니다.
array = [:peanut, :butter, :and, :jelly]
array[0] #=> :peanut #OK!
array[0,1] #=> [:peanut] #OK!
array[0,2] #=> [:peanut, :butter] #OK!
array[0,0] #=> [] #OK!
array[2] #=> :and #OK!
array[2,2] #=> [:and, :jelly] #OK!
array[2,20] #=> [:and, :jelly] #OK!
array[4] #=> nil #OK!
array[4,0] #=> [] #HUH?? Why's that?
array[4,100] #=> [] #Still HUH, but consistent with previous one
array[5] #=> nil #consistent with array[4] #=> nil
array[5,0] #=> nil #WOW. Now I don't understand anything anymore...
왜 그럴까요?array[5,0]
와같않과 같지 array[4,0]
(길이+1)th 위치에서 시작할 때 어레이 슬라이싱이 이렇게 이상하게 동작하는 이유가 있습니까?
슬라이싱과 인덱싱은 서로 다른 작업이며, 이 작업의 동작을 서로 유추하는 것이 문제입니다.
슬라이스의 첫 번째 인수는 요소가 아니라 요소 사이의 위치를 식별하며 범위를 정의합니다(요소 자체는 아님).
:peanut :butter :and :jelly
0 1 2 3 4
4는 여전히 배열 내에 있으며, 겨우 0개의 요소를 요청하면 배열의 빈 끝을 얻을 수 있습니다.하지만 지수 5가 없어서 거기서부터 자를 수 없습니다.
을 할 예:array[4]
자체를 까지밖에 가지 않습니다.), 자에까체가원리로지으므있수고소키를 0서지 3다표니시됩만는▁),까다표에니▁you▁them시.
이는 slice가 Array#slice에서 어레이, 관련 소스 문서를 반환한다는 사실과 관련이 있습니다.
* call-seq:
* array[index] -> obj or nil
* array[start, length] -> an_array or nil
* array[range] -> an_array or nil
* array.slice(index) -> obj or nil
* array.slice(start, length) -> an_array or nil
* array.slice(range) -> an_array or nil
그것은 만약 당신이 경계를 벗어난 시작을 한다면, 그것은 0으로 돌아갈 것이고, 따라서 당신의 예에서.array[4,0]
에서는 존재하는 4번째 요소를 요청하지만 0개의 요소 배열을 반환하도록 요청합니다.하는 동안에array[5,0]
인덱스가 범위를 벗어났으므로 인덱스가 0을 반환합니다.슬라이스 방법이 원래 데이터 구조를 변경하는 것이 아니라 새 배열을 반환하는 것임을 기억하는 경우 이 방법이 더 타당할 수 있습니다.
편집:
댓글을 검토한 후 이 답변을 편집하기로 했습니다.Slice는 arg 값이 2일 때 다음 코드 스니펫을 호출합니다.
if (argc == 2) {
if (SYMBOL_P(argv[0])) {
rb_raise(rb_eTypeError, "Symbol as array index");
}
beg = NUM2LONG(argv[0]);
len = NUM2LONG(argv[1]);
if (beg < 0) {
beg += RARRAY(ary)->len;
}
return rb_ary_subseq(ary, beg, len);
}
에서 array.c
가 있는 rb_ary_subseq
메소드가 정의되면 길이가 인덱스가 아닌 범위를 벗어나면 메소드가 0으로 반환됩니다.
if (beg > RARRAY_LEN(ary)) return Qnil;
이 경우 4가 전달되면 4개의 요소가 있는지 확인하여 0 반환을 트리거하지 않습니다.그런 다음 두 번째 arg가 0으로 설정된 경우 계속되고 빈 배열을 반환합니다.5를 전달하면 배열에 5개의 요소가 없으므로 0 인수를 평가하기 전에 0을 반환합니다.여기 944번에 있는 코드입니다.
저는 이것이 버그이거나 적어도 예측할 수 없으며 '최소한의 놀라움의 원칙'이 아니라고 믿습니다.몇 분 후면 루비 코어에 실패한 테스트 패치를 제출할 것입니다.
적어도 동작이 일관적이라는 점에 유의해야 합니다.합니다; 한 것은 5시에 합니다. 이상한 것은 오직 에서 발생합니다.[4,N]
.
이런 패턴이 도움이 될 수도 있고, 그냥 피곤해서 전혀 도움이 되지 않을 수도 있습니다.
array[0,4] => [:peanut, :butter, :and, :jelly]
array[1,3] => [:butter, :and, :jelly]
array[2,2] => [:and, :jelly]
array[3,1] => [:jelly]
array[4,0] => []
에서[4,0]
우리는 배열의 끝을 잡습니다.패턴의 아름다움에 관한 한, 만약 마지막 하나가 돌아온다면, 나는 그것이 오히려 이상하다고 생각할 것입니다.nil
같은 맥락 에, 때문에상황이.4
빈 배열을 반환할 수 있도록 첫 번째 매개 변수에 사용할 수 있는 옵션입니다.그러나 5 이상이 되면 그 방법은 완전히 그리고 완전히 범위를 벗어났기 때문에 즉시 종료될 가능성이 높습니다.
배열 슬라이스가 r 값뿐만 아니라 유효한 l 값이 될 수 있다는 점을 고려할 때 이는 타당합니다.
array = [:peanut, :butter, :and, :jelly]
# replace 0 elements starting at index 5 (insert at end or array):
array[4,0] = [:sandwich]
# replace 0 elements starting at index 0 (insert at head of array):
array[0,0] = [:make, :me, :a]
# array is [:make, :me, :a, :peanut, :butter, :and, :jelly, :sandwich]
# this is just like replacing existing elements:
array[3, 4] = [:grilled, :cheese]
# array is [:make, :me, :a, :grilled, :cheese, :sandwich]
이런 일은 가능하지 않을 것입니다array[4,0]
된 환된반nil
에 []
.만지하,array[5,0]
아온다를 합니다.nil
범위를 벗어났기 때문입니다(4요소 배열의 4번째 요소 뒤에 삽입하는 것은 의미가 있지만 4요소 배열의 5번째 요소 뒤에 삽입하는 것은 의미가 없습니다).
구문 array[x,y]
에 시작"으로x
의 array
지까고다까지 합니다.y
요소".이는 다음과 같은 경우에만 의미가 있습니다.array
적어도 가지고 있습니다.x
요소들.
이것은 말이 됩니다.
문자열의 시작과 끝에 길이가 0인 표현식이 사용되도록 정의하려면 이러한 조각을 할당할 수 있어야 합니다.
array[4, 0] = :sandwich
array[0, 0] = :crunchy
=> [:crunchy, :peanut, :butter, :and, :jelly, :sandwich]
게리 라이트의 설명도 큰 도움이 되었습니다.http://www.ruby-forum.com/topic/1393096#990065
게리 라이트의 대답은 -입니다.
http://www.ruby-doc.org/core/classes/Array.html
문서는 확실히 더 명확할 수 있지만 실제 행동은 자기 일관성이 있고 유용합니다.참고: String의 1.9.X 버전을 가정합니다.
다음과 같은 방법으로 번호 지정을 고려할 수 있습니다.
-4 -3 -2 -1 <-- numbering for single argument indexing
0 1 2 3
+---+---+---+---+
| a | b | c | d |
+---+---+---+---+
0 1 2 3 4 <-- numbering for two argument indexing or start of range
-4 -3 -2 -1
일반적인 (그리고 이해할 수 있는) 실수는 단일 인수 인덱스의 의미가 두 인수 시나리오(또는 범위)의 첫 번째 인수의 의미와 동일하다고 너무 가정합니다.이들은 실제로 동일한 것이 아니며 문서에는 이를 반영하지 않습니다.오류는 분명히 설명서에 있지만 구현에는 없습니다.
단일 인수: 인덱스는 문자열 내의 단일 문자 위치를 나타냅니다.결과는 인덱스에서 발견된 단일 문자열이거나 지정된 인덱스에 문자가 없기 때문에 0입니다.
s = ""
s[0] # nil because no character at that position
s = "abcd"
s[0] # "a"
s[-4] # "a"
s[-5] # nil, no characters before the first one
두 개의 정수 인수: 인수는 추출하거나 바꿀 문자열의 일부를 식별합니다.특히 문자열의 너비가 0인 부분도 식별할 수 있으므로 문자열의 앞이나 끝을 포함하여 기존 문자 앞이나 뒤에 텍스트를 삽입할 수 있습니다.이 경우 첫 번째 인수는 문자 위치를 식별하지 않고 위의 다이어그램에 표시된 것처럼 문자 사이의 공백을 식별합니다.두 번째 인수는 0일 수 있는 길이입니다.
s = "abcd" # each example below assumes s is reset to "abcd"
To insert text before 'a': s[0,0] = "X" # "Xabcd"
To insert text after 'd': s[4,0] = "Z" # "abcdZ"
To replace first two characters: s[0,2] = "AB" # "ABcd"
To replace last two characters: s[-2,2] = "CD" # "abCD"
To replace middle two characters: s[1..3] = "XX" # "aXXd"
범위의 행동은 꽤 흥미롭습니다.두 개의 인수가 제공되는 경우(위에서 설명한 대로) 시작점은 첫 번째 인수와 동일하지만 범위의 끝점은 단일 인덱싱의 경우 '문자 위치'이거나 두 개의 정수 인수의 경우 '에지 위치'일 수 있습니다.차이는 이중 점 범위를 사용할지 또는 삼중 점 범위를 사용할지 여부에 따라 결정됩니다.
s = "abcd"
s[1..1] # "b"
s[1..1] = "X" # "aXcd"
s[1...1] # ""
s[1...1] = "X" # "aXbcd", the range specifies a zero-width portion of
the string
s[1..3] # "bcd"
s[1..3] = "X" # "aX", positions 1, 2, and 3 are replaced.
s[1...3] # "bc"
s[1...3] = "X" # "aXd", positions 1, 2, but not quite 3 are replaced.
이러한 예를 다시 살펴보고 이중 또는 범위 인덱싱 예제에 대해 단일 인덱스 의미론을 사용한다고 주장하면 혼란이 발생할 수 있습니다.실제 동작을 모델링하려면 아스키 다이어그램에 표시된 대체 번호를 사용해야 합니다.
이 동작이 이상한 것 같다는 데 동의하지만 의 공식 문서에서도 아래 "특별한 경우"의 예와 동일한 동작을 보여줍니다.
a = [ "a", "b", "c", "d", "e" ]
a[2] + a[0] + a[1] #=> "cab"
a[6] #=> nil
a[1, 2] #=> [ "b", "c" ]
a[1..3] #=> [ "b", "c", "d" ]
a[4..7] #=> [ "e" ]
a[6..10] #=> nil
a[-3, 3] #=> [ "c", "d", "e" ]
# special cases
a[5] #=> nil
a[5, 1] #=> []
a[5..10] #=> []
인 불하게도묘, 의조차도사그들행.Array#slice
이러한 방식으로 작동하는 이유에 대한 통찰력을 제공하지 않는 것 같습니다.
요소 참조—인덱스에서 요소를 반환하거나, 길이 요소에 대해 시작하여 계속되는 하위 배열을 반환하거나, 범위로 지정된 하위 배열을 반환합니다.음수 인덱스는 배열의 끝에서 거꾸로 카운트됩니다(-1은 마지막 요소).인덱스(또는 시작 인덱스)가 범위를 벗어나면 0을 반환합니다.
Jim Weirich가 제공한 설명
한 가지 방법은 인덱스 위치 4가 배열의 가장 가장자리에 있다는 것입니다.슬라이스를 요청할 때 남은 어레이의 양만큼 반환합니다.어레이[2,10], 어레이[3,10] 및 어레이[4,10]를 고려해 보십시오.각각 배열 끝 부분의 나머지 비트(각각 2개, 1개, 0개)를 반환합니다.그러나 위치 5는 분명히 배열 외부에 있고 가장자리에 있지 않으므로 배열 [5,10]은 0을 반환합니다.
다음 배열을 고려합니다.
>> array=["a","b","c"]
=> ["a", "b", "c"]
있)목 항 당 삽 수 할 니 다 입 드 을 하 여 ▁you 니 다 있 ▁an ▁by 습 헤 ▁assigning ▁to ▁insert 에 ▁it 드 ) ) ▁array head 수 ▁item 다 ▁the ▁to ▁can ▁of 음 ▁thea[0,0]
요소를 중간에 배치합니다."a"
그리고."b"
,사용하다a[1,0]
기적으로라는 에서는 에서법표기본서▁basically에▁in,▁notation▁the.a[i,n]
,i
와 인를나니다냅을 나타냅니다.n
다수의 원소언제n=0
배열 요소 사이의 위치를 정의합니다.
이제 배열의 끝을 생각해보면 위에서 설명한 표기법을 사용하여 항목을 끝에 추가할 수 있는 방법은 무엇입니까?을 순단, 값할에 합니다.a[3,0]
이것은 배열의 꼬리입니다.
그래서, 만약 당신이 그 요소에 접근하려고 한다면,a[3,0]
얻게 될 것입니다[]
이 경우에도 여전히 배열 범위에 있습니다.하지만 당신이 접근하려고 한다면,a[4,0]
얻게 될 것입니다nil
더 이상 배열의 범위에 속하지 않기 때문에 반환 값으로 사용할 수 있습니다.
자세한 내용은 http://mybrainstormings.wordpress.com/2012/09/10/arrays-in-ruby/ 을 참조하십시오.
tl;dr: 의 소스 코드에서, 1 또는 2 인수를 에 전달할지에 따라 다른 함수가 호출됩니다.Array#slice
예기치 않은 반환 값이 발생합니다.
(먼저, 저는 C로 코딩하지 않고 몇 년 동안 루비를 사용해 왔습니다.따라서 C에 익숙하지 않지만 함수와 변수의 기본을 익히는 데 몇 분이 걸린다면 아래에 설명된 것처럼 루비 소스 코드를 따르는 것은 정말 어렵지 않습니다.이 답변은 Ruby v2.3을 기반으로 하지만 v1.9와 다소 동일합니다.)
시나리오 #1
array.length == 4; array.slice(4) #=> nil
보면를드의 ,Array#slice
() 하나의 인수만 전달되는 경우(1277-1289행) 인덱스 값(양 또는 음일 수 있음)을 전달하여 호출되는 것을 확인할 수 있습니다.rb_ary_aref
rb_ary_entry
그런 다음 배열의 시작 부분에서 요청된 요소의 위치를 계산한 다음(즉, 음수 인덱스가 전달된 경우 양의 등가물을 계산함), 요청된 요소를 가져오도록 호출합니다.
예상대로, 반환됩니다.nil
가 배의길다음같을과때가이열 ▁the때같을▁oflen
인덱스보다 작거나 같음(여기서 이를 가리킴offset
).
1189: if (offset < 0 || len <= offset) {
1190: return Qnil;
1191: }
시나리오 #2
array.length == 4; array.slice(4, 0) #=> []
의 인수가 때 시작 인덱스는 "" " " " " " " " " " (", " " " " " )beg
len
)를 호출합니다.
in, 시작 인덱스인 경우beg
배열 길이보다 큼alen
,nil
반환됨:
1208: long alen = RARRAY_LEN(ary);
1209:
1210: if (beg > alen) return Qnil;
않으면 가 렇지않면결슬라길의스이이과그으▁otherwise길이▁of의▁length.len
되면 빈 반환됩니다.: " " " " " " 으 " 계 " 되 " 산 " 이 " 고 " 됩 " 니 " 환 " 다 " 반 " 이 " 열 " 배 " 빈 " 면 " 로 " 되 " 결 " 정 " ▁is "
1213: if (alen < len || alen < beg + len) {
1214: len = alen - beg;
1215: }
1216: klass = rb_obj_class(ary);
1217: if (len == 0) return ary_new(klass, 0);
따서시가라 보다 크지 않기 에 기때않 4문다 입크니 지다보 다음작 지수▁so 때니다.array.length
다음 대신 빈 배열이 반환됩니다.nil
기대할 수 있는 가치
질문에 답했습니까?
여기서 실제 질문이 "어떤 코드가 이것을 발생시키는가?"가 아니라 "마츠가 왜 이런 식으로 했을까?"라면 다음 루비콘프에서 커피를 사주고 그에게 물어봐야 할 것입니다.
언급URL : https://stackoverflow.com/questions/3568222/array-slicing-in-ruby-explanation-for-illogical-behaviour-taken-from-rubykoans
'programing' 카테고리의 다른 글
Gitdiff는 하위 프로젝트가 더럽다고 말합니다. (0) | 2023.05.29 |
---|---|
VB.NET 대 C# 정수 나눗셈 (0) | 2023.05.29 |
Xcode에서 경고를 억제하는 방법이 있습니까? (0) | 2023.05.24 |
현재 폴더 경로 가져오기 (0) | 2023.05.24 |
postgres 기본 표준 시간대 (0) | 2023.05.24 |