Skip to content

Commit ff1588c

Browse files
feat(deque): add drain
1 parent 5ed8e48 commit ff1588c

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

deque/deque.mbt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,3 +1355,71 @@ pub fn[A] T::flatten(self : T[T[A]]) -> T[A] {
13551355
}
13561356
target
13571357
}
1358+
1359+
///|
1360+
/// Removes and returns elements in the specified range [begin, end) from the deque.
1361+
///
1362+
/// Parameters:
1363+
///
1364+
/// * `self` : The target deque (modified in-place).
1365+
/// * `start` : Start index of the range (inclusive).
1366+
/// * `len` : Length of the range to drain.
1367+
///
1368+
/// Important:
1369+
/// - Returns a new deque containing the drained elements.
1370+
/// - Original deque retains elements outside [start, start + len) in original order.
1371+
///
1372+
/// Example:
1373+
///
1374+
/// ```moonbit
1375+
/// let deque = @deque.of([1,2,3,4,5,6,7,8,9])
1376+
/// let deque_test = deque.drain(start=2, len=4)
1377+
/// inspect(deque_test, content="@deque.of([3, 4, 5, 6])")
1378+
/// inspect(deque, content="@deque.of([1, 2, 7, 8, 9])")
1379+
/// ```
1380+
pub fn[A] T::drain(self : T[A], start~ : Int, len? : Int) -> T[A] {
1381+
let len = match len {
1382+
Some(l) => if l > self.len { self.len } else { l }
1383+
None => self.len - start
1384+
}
1385+
if len == 0 {
1386+
return new()
1387+
}
1388+
let deque = T::{
1389+
buf: UninitializedArray::make(len),
1390+
len,
1391+
head: 0,
1392+
tail: len - 1,
1393+
}
1394+
let cap = self.buf.length()
1395+
let start_idx = (self.head + start) % cap
1396+
let start_len = cap - start_idx
1397+
1398+
// copy to deque
1399+
if start_len < len {
1400+
deque.buf.unsafe_blit(0, self.buf, start_idx, start_len)
1401+
deque.buf.unsafe_blit(start_len, self.buf, 0, len - start_len)
1402+
for i in 0..<start_len {
1403+
set_null(self.buf, (start_idx + i) % cap)
1404+
}
1405+
for i in 0..<(len - start_len) {
1406+
set_null(self.buf, i)
1407+
}
1408+
} else {
1409+
deque.buf.unsafe_blit(0, self.buf, start_idx, len)
1410+
for i in 0..<len {
1411+
set_null(self.buf, start_idx + i)
1412+
}
1413+
}
1414+
1415+
// move self
1416+
// TODO: use blit
1417+
let new_head = self.head + len
1418+
for i = start - 1; i >= 0; i = i - 1 {
1419+
self.buf[(new_head + i) % cap] = self.buf[(self.head + i) % cap]
1420+
set_null(self.buf, (self.head + i) % cap)
1421+
}
1422+
self.head = new_head % cap
1423+
self.len -= len
1424+
deque
1425+
}

deque/deque_test.mbt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,3 +1021,30 @@ test "flatten when wrapped around" {
10211021
content="@deque.of([6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5])",
10221022
)
10231023
}
1024+
1025+
///|
1026+
test "drain when wrapped around" {
1027+
// when the area to be drained is wrapped around
1028+
let dq = of([4, 5, 6])
1029+
dq..push_front(3)..push_front(2)..push_front(1)
1030+
inspect(dq.as_views(), content="([1, 2, 3], [4, 5, 6])")
1031+
let dq2 = dq.drain(start=2, len=2)
1032+
inspect(dq.as_views(), content="([1], [2, 5, 6])")
1033+
inspect(dq2, content="@deque.of([3, 4])")
1034+
1035+
// when the area to be drained is not wrapped around
1036+
let dq = of([4, 5, 6])
1037+
dq..push_front(3)..push_front(2)..push_front(1)
1038+
inspect(dq.as_views(), content="([1, 2, 3], [4, 5, 6])")
1039+
let dq2 = dq.drain(start=1, len=2)
1040+
inspect(dq.as_views(), content="([1], [4, 5, 6])")
1041+
inspect(dq2, content="@deque.of([2, 3])")
1042+
1043+
// when the start is larger than len
1044+
let dq = of([5, 6])
1045+
dq..push_front(4)..push_front(3)..push_front(2)..push_front(1)
1046+
inspect(dq.as_views(), content="([1, 2], [3, 4, 5, 6])")
1047+
let dq2 = dq.drain(start=3, len=2)
1048+
inspect(dq.as_views(), content="([1, 2, 3, 6], [])")
1049+
inspect(dq2, content="@deque.of([4, 5])")
1050+
}

0 commit comments

Comments
 (0)