mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
This commit is contained in:
parent
e03082c941
commit
fa2709f182
8 changed files with 367 additions and 216 deletions
|
|
@ -32,100 +32,6 @@ Creates a new source state with the given value.
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## derive()
|
||||
|
||||
Derives a new state from existing states.
|
||||
|
||||
- ### Type
|
||||
|
||||
```lua
|
||||
function derive<T>(source: () -> T): () -> T
|
||||
```
|
||||
|
||||
- ### Details
|
||||
|
||||
The derived state will have its value recalculated when any source state it
|
||||
derives from is updated.
|
||||
|
||||
Anytime its value is recalculated it is also cached, subsequent calls will
|
||||
retun this cached value until it recalculates again.
|
||||
|
||||
Takes a callback that is immediately run to determine what states are being
|
||||
referenced.
|
||||
|
||||
> ⚠️ Non-yielding.
|
||||
|
||||
- ### Example
|
||||
|
||||
```lua
|
||||
local count = wrap(0)
|
||||
local text = derive(function() return `count: {count()}` end)
|
||||
|
||||
text() -- "count: 0"
|
||||
|
||||
count(1)
|
||||
|
||||
text() -- "count: 1"
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## map()
|
||||
|
||||
Maps each value in a table state to a new table state.
|
||||
|
||||
- ### Type
|
||||
|
||||
```lua
|
||||
function map<KI, VI, VO>(
|
||||
source: () -> Map<KI, VI>,
|
||||
transform: (value: () -> VI, index: KI) -> VO
|
||||
): Map<KI, VO>
|
||||
|
||||
- ### Details
|
||||
|
||||
The transform function is called only ever *once* for each index in the
|
||||
source table. The first argument is a state containing the index's value and
|
||||
the second argument is just the index.
|
||||
|
||||
Anytime a new index is added, the transform function will be called again for
|
||||
that new index.
|
||||
|
||||
Anytime an existing index changes, the transform function is not rerun,
|
||||
instead the passed state for that index will update, causing anything
|
||||
depending on it to update too.
|
||||
|
||||
Returns a state containing the mapped key-value pairs.
|
||||
|
||||
> ⚠️ Non-yielding.
|
||||
|
||||
- ### Example
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
name: string,
|
||||
icon: number
|
||||
}
|
||||
|
||||
local items = source {} :: () -> Array<Item>
|
||||
|
||||
local displays = map(numbers, function(item, i)
|
||||
return ItemDisplay {
|
||||
Name = function()
|
||||
return item().name
|
||||
end,
|
||||
|
||||
Image = function()
|
||||
return "rbxassetid://" .. item().icon
|
||||
end,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## watch()
|
||||
|
||||
Runs a callback on state change.
|
||||
|
|
@ -166,3 +72,179 @@ Runs a callback on state change.
|
|||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## derive()
|
||||
|
||||
Derives a new state from existing states.
|
||||
|
||||
- ### Type
|
||||
|
||||
```lua
|
||||
function derive<T>(source: () -> T): () -> T
|
||||
```
|
||||
|
||||
- ### Details
|
||||
|
||||
The derived state will have its value recalculated when any source state it
|
||||
derives from is updated.
|
||||
|
||||
Anytime its value is recalculated it is also cached, subsequent calls will
|
||||
retun this cached value until it recalculates again.
|
||||
|
||||
Takes a callback that is immediately run to determine what states are being
|
||||
referenced.
|
||||
|
||||
> ⚠️ Non-yielding.
|
||||
|
||||
- ### Example
|
||||
|
||||
```lua
|
||||
local count = wrap(0)
|
||||
local text = derive(function() return `count: {count()}` end)
|
||||
|
||||
text() -- "count: 0"
|
||||
|
||||
count(1)
|
||||
|
||||
text() -- "count: 1"
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## indexes()
|
||||
|
||||
Maps each index in a table to an object.
|
||||
|
||||
- ### Type
|
||||
|
||||
```lua
|
||||
function indexes<KI, VI, VO>(
|
||||
source: () -> Map<KI, VI>,
|
||||
transform: (value: () -> VI, index: KI) -> VO
|
||||
): Array<VO>
|
||||
|
||||
- ### Details
|
||||
|
||||
The transform function is called only ever *once* for each index in the
|
||||
source table. The first argument is a state containing the index's value and
|
||||
the second argument is just the index.
|
||||
|
||||
Anytime a new index is added, the transform function will be called again for
|
||||
that new index.
|
||||
|
||||
Anytime an existing index value changes, the transform function is not rerun,
|
||||
instead the passed state for that index will update, causing anything
|
||||
depending on it to update too.
|
||||
|
||||
Returns a state containing an array of all objects returned by the transform.
|
||||
|
||||
> ⚠️ Non-yielding.
|
||||
|
||||
- ### Example
|
||||
|
||||
The intended purpose of this function is to map each index in a table to
|
||||
a UI element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
name: string,
|
||||
icon: number
|
||||
}
|
||||
|
||||
local items = source {} :: () -> Array<Item>
|
||||
|
||||
local displays = indexes(numbers, function(item, i)
|
||||
return ItemDisplay {
|
||||
Name = function()
|
||||
return item().name
|
||||
end,
|
||||
|
||||
Image = function()
|
||||
return "rbxassetid://" .. item().icon
|
||||
end,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## values()
|
||||
|
||||
Maps each value in a table to an object.
|
||||
|
||||
- ### Type
|
||||
|
||||
```lua
|
||||
function values<KI, VI, VO>(
|
||||
source: () -> Map<KI, VI>,
|
||||
transform: (value: VI, index: () -> KI) -> VO
|
||||
): Array<VO>
|
||||
|
||||
- ### Details
|
||||
|
||||
The transform function is called only ever *once* for each value in the
|
||||
source table. The first argument is the index's value and
|
||||
the second argument is a state containing the index.
|
||||
|
||||
Anytime a new value is added, the transform function will be called again
|
||||
for that new value.
|
||||
|
||||
Anytime an existing value's index changes, the transform function is not
|
||||
rerun, instead the passed state for that value will update, causing anything
|
||||
depending on it to update too.
|
||||
|
||||
Returns a state containing an array of all objects returned by the transform.
|
||||
|
||||
> ⚠️ Non-yielding.
|
||||
|
||||
- ### Example
|
||||
|
||||
The intended purpose of this function is to map each value in a table to
|
||||
a UI element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
name: string,
|
||||
icon: number
|
||||
}
|
||||
|
||||
local items = source {} :: () -> Array<Item>
|
||||
|
||||
local displays = values(numbers, function(item, i)
|
||||
return ItemDisplay {
|
||||
Name = item.Name
|
||||
|
||||
Image = "rbxassetid://" .. item.icon,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
- ### Extra
|
||||
|
||||
When should you use `indexes()` and `values()`?
|
||||
|
||||
`values()` should be used when you have a fixed set of objects where the
|
||||
same objects can be re-arranged in the source table. It maps a value to a
|
||||
UI element.
|
||||
|
||||
e.g.
|
||||
- List of all players.
|
||||
- Inventory of items.
|
||||
- Chat message history.
|
||||
- Toast notifications.
|
||||
|
||||
`indexes()` should be used in other cases, especially when your source table
|
||||
has primitive value. It maps an index to a UI element.
|
||||
|
||||
e.g.
|
||||
- List of character or weapon stats.
|
||||
|
||||
In most cases, both functions will appear to have the same behavior.
|
||||
The main difference is performance, picking the right function to use can
|
||||
result in less property updates and less re-renders.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ type Item = {
|
|||
local items = source({} :: Array<Item>)
|
||||
|
||||
List {
|
||||
Children = map(items, function(item, i)
|
||||
Children = indexes(items, function(item, i)
|
||||
return create "ImageLabel" {
|
||||
Image = function()
|
||||
return "rbxassetid://" .. item().Icon
|
||||
|
|
|
|||
|
|
@ -88,32 +88,32 @@ local function bind_parent(instance: Instance, fn: () -> Instance?)
|
|||
end
|
||||
|
||||
local function bind_children(parent: Instance, fn: () -> { Instance })
|
||||
local currentChildrenSet: { [Instance]: true } = {} -- cache of all children parented before update
|
||||
local newChildrenSet: { [Instance]: true } = {} -- cache of all children parented after update
|
||||
local current_child_set: { [Instance]: true } = {} -- cache of all children parented before update
|
||||
local new_child_set: { [Instance]: true } = {} -- cache of all children parented after update
|
||||
|
||||
setup(parent, function(parent_weak)
|
||||
local newChildren = fn() -- all (and only) children that should be parented after this update
|
||||
if newChildren and type(newChildren) ~= "table" then
|
||||
throw(`Cannot parent instance of type { type(newChildren) } `)
|
||||
local new_childs = fn() -- all (and only) children that should be parented after this update
|
||||
if new_childs and type(new_childs) ~= "table" then
|
||||
throw(`Cannot parent instance of type { type(new_childs) } `)
|
||||
end
|
||||
|
||||
if newChildren then
|
||||
for _, child in next, newChildren do
|
||||
newChildrenSet[child] = true -- record child set from this update
|
||||
if not currentChildrenSet[child] then
|
||||
if new_childs then
|
||||
for _, child in next, new_childs do
|
||||
new_child_set[child] = true -- record child set from this update
|
||||
if not current_child_set[child] then
|
||||
child.Parent = parent_weak -- if child wasn't already parented then parent it
|
||||
else
|
||||
currentChildrenSet[child] = nil -- remove child from cache if it was already in cache
|
||||
current_child_set[child] = nil -- remove child from cache if it was already in cache
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for child in next, currentChildrenSet do
|
||||
for child in next, current_child_set do
|
||||
child.Parent = nil -- unparent all children that weren't in the new children set
|
||||
end
|
||||
|
||||
table.clear(currentChildrenSet) -- clear cache, preserve capacity
|
||||
currentChildrenSet, newChildrenSet = newChildrenSet, currentChildrenSet
|
||||
table.clear(current_child_set) -- clear cache, preserve capacity
|
||||
current_child_set, new_child_set = new_child_set, current_child_set
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
|||
65
src/map.luau
65
src/map.luau
|
|
@ -1,65 +0,0 @@
|
|||
if not game then script = (require :: any) "test/wrap-require" end
|
||||
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create = graph.create
|
||||
local set = graph.set
|
||||
local capture = graph.capture
|
||||
local link = graph.link
|
||||
|
||||
type Map<K, V> = { [K]: V }
|
||||
|
||||
local function map<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI, K) -> VO): () -> Map<K, VO>
|
||||
local input_cache = {} :: Map<K, VI>
|
||||
local output_cache = {} :: Map<K, VO>
|
||||
local input_nodes = {} :: Map<K, Node<VI>>
|
||||
local remove_queue = {} :: { K }
|
||||
|
||||
local function recompute(data)
|
||||
-- queue removed values
|
||||
for k in next, input_cache do
|
||||
if data[k] == nil then
|
||||
table.insert(remove_queue, k)
|
||||
end
|
||||
end
|
||||
|
||||
-- remove queued values
|
||||
for _, k in next, remove_queue do
|
||||
input_cache[k] = nil
|
||||
output_cache[k] = nil
|
||||
input_nodes[k] = nil
|
||||
end
|
||||
|
||||
-- process new or changed values
|
||||
for k, v in next, data do
|
||||
if input_cache[k] == nil then
|
||||
local node, get_value = create(v)
|
||||
input_nodes[k] = node
|
||||
output_cache[k] = transform(get_value, k)
|
||||
elseif input_cache[k] ~= v then
|
||||
set(input_nodes[k], v)
|
||||
end
|
||||
input_cache[k] = v
|
||||
end
|
||||
|
||||
return output_cache
|
||||
end
|
||||
|
||||
local function derive()
|
||||
return recompute(input())
|
||||
end
|
||||
|
||||
local output, output_get = create(output_cache)
|
||||
|
||||
local nodes, value = capture(input)
|
||||
|
||||
for _, node in next, nodes do
|
||||
link(node, output, derive)
|
||||
end
|
||||
|
||||
output.cache = recompute(value)
|
||||
|
||||
return output_get
|
||||
end
|
||||
|
||||
return map
|
||||
|
|
@ -9,13 +9,13 @@ local link = graph.link
|
|||
|
||||
type Map<K, V> = { [K]: V }
|
||||
|
||||
-- todo: optimize, double buffering?
|
||||
|
||||
-- todo: this could be optimized
|
||||
local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI, K) -> VO): () -> { VO }
|
||||
local input_cache = {} :: Map<K, VI>
|
||||
local output_cache = {} :: Map<K, VO>
|
||||
local input_nodes = {} :: Map<K, Node<VI>>
|
||||
local remove_queue = {} :: { K }
|
||||
local output_array = {} :: { VO }
|
||||
|
||||
local function recompute(data)
|
||||
-- queue removed values
|
||||
|
|
@ -34,23 +34,25 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
-- process new or changed values
|
||||
for k, v in next, data do
|
||||
if input_cache[k] == nil then
|
||||
local cv = input_cache[k]
|
||||
if cv == nil then
|
||||
local node, get_value = create(v)
|
||||
input_nodes[k] = node
|
||||
output_cache[k] = transform(get_value, k)
|
||||
elseif input_cache[k] ~= v then
|
||||
input_cache[k] = v
|
||||
elseif cv ~= v then
|
||||
set(input_nodes[k], v)
|
||||
input_cache[k] = v
|
||||
end
|
||||
input_cache[k] = v
|
||||
end
|
||||
|
||||
local output = {}
|
||||
|
||||
-- output elements
|
||||
table.clear(output_array)
|
||||
for _, v in next, output_cache do
|
||||
table.insert(output, v)
|
||||
table.insert(output_array, v)
|
||||
end
|
||||
|
||||
return output
|
||||
return output_array
|
||||
end
|
||||
|
||||
local function derive()
|
||||
|
|
@ -70,50 +72,54 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
return output_get
|
||||
end
|
||||
|
||||
-- todo: this should be optimized
|
||||
local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () -> K) -> VO): () -> { VO }
|
||||
local input_cache = {} :: Map<VI, K>
|
||||
local input_cache_up = {} :: Map<VI, K>
|
||||
local input_cache_buffer_up = {} :: Map<VI, K>
|
||||
|
||||
local output_cache = {} :: Map<VI, VO>
|
||||
local input_nodes = {} :: Map<VI, Node<K>>
|
||||
local remove_queue = {} :: { VI }
|
||||
local output_array = {} :: { VO }
|
||||
|
||||
local function recompute(data: Map<K, VI>)
|
||||
local inverted_data = {}
|
||||
local input_cache, input_cache_buffer =
|
||||
input_cache_up, input_cache_buffer_up
|
||||
|
||||
-- process new or changed values
|
||||
for i, v in next, data do
|
||||
if input_cache[v] == nil then
|
||||
local cv = input_cache[v]
|
||||
|
||||
if cv == nil then
|
||||
local node, get_value = create(i)
|
||||
input_nodes[v] = node
|
||||
input_cache[v] = i
|
||||
output_cache[v] = transform(v, get_value)
|
||||
elseif input_cache[v] ~= i then
|
||||
elseif cv ~= i then
|
||||
set(input_nodes[v], i)
|
||||
end
|
||||
|
||||
inverted_data[v] = i
|
||||
|
||||
input_cache_buffer[v] = i
|
||||
end
|
||||
|
||||
-- queue removed values
|
||||
-- remove old values
|
||||
for v, k in next, input_cache do
|
||||
if inverted_data[v] == nil then
|
||||
table.insert(remove_queue, v)
|
||||
if input_cache_buffer[v] == nil then
|
||||
output_cache[v] = nil
|
||||
input_nodes[v] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- remove queued values
|
||||
for _, k in next, remove_queue do
|
||||
input_cache[k] = nil
|
||||
output_cache[k] = nil
|
||||
input_nodes[k] = nil
|
||||
end
|
||||
-- update buffer cache
|
||||
table.clear(input_cache)
|
||||
input_cache_up, input_cache_buffer_up = input_cache_buffer, input_cache
|
||||
|
||||
local output = {}
|
||||
-- output elements
|
||||
table.clear(output_array)
|
||||
|
||||
for _, v in next, output_cache do
|
||||
table.insert(output, v)
|
||||
table.insert(output_array, v)
|
||||
end
|
||||
|
||||
return output
|
||||
return output_array
|
||||
end
|
||||
|
||||
local function derive()
|
||||
|
|
|
|||
|
|
@ -176,9 +176,9 @@ local function update_springs(dt: number)
|
|||
|
||||
set(output, value)
|
||||
|
||||
if new_alpha > 0.9 then
|
||||
table.insert(remove_queue, data)
|
||||
end
|
||||
-- if new_alpha > 0.9 then
|
||||
-- table.insert(remove_queue, data)
|
||||
-- end
|
||||
end
|
||||
|
||||
for _, data in next, remove_queue do
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ local BENCH, START = require("test/testkit").benchmark()
|
|||
|
||||
local vide = require "src/init"
|
||||
|
||||
local N = 1e6
|
||||
local N = 2^18 -- 262144
|
||||
|
||||
BENCH("Create state", function()
|
||||
local cache = table.create(N)
|
||||
|
|
@ -17,7 +17,7 @@ BENCH("Create state", function()
|
|||
end
|
||||
end)
|
||||
|
||||
BENCH("Get state value", function()
|
||||
BENCH("Get value", function()
|
||||
local state = vide.source(1)
|
||||
|
||||
for i = 1, START(N) do
|
||||
|
|
@ -25,7 +25,7 @@ BENCH("Get state value", function()
|
|||
end
|
||||
end)
|
||||
|
||||
BENCH("Set state value", function()
|
||||
BENCH("Set value", function()
|
||||
local state = vide.source(1)
|
||||
|
||||
for i = 1, START(N) do
|
||||
|
|
@ -45,20 +45,22 @@ BENCH("Derive 1 state", function()
|
|||
end
|
||||
end)
|
||||
|
||||
BENCH("Derive 2 states", function()
|
||||
BENCH("Derive 4 states", function()
|
||||
local cache = table.create(N)
|
||||
local state = vide.source(1)
|
||||
local state2 = vide.source(2)
|
||||
local state3 = vide.source(3)
|
||||
local state4 = vide.source(4)
|
||||
local derive = vide.derive
|
||||
|
||||
for i = 1, START(N) do
|
||||
cache[i] = derive(function()
|
||||
return state() + state2()
|
||||
return state() + state2() + state3() + state4()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
BENCH("Set state value derived", function()
|
||||
BENCH("Set derived value", function()
|
||||
local state = vide.source(1)
|
||||
local _derived = vide.derive(state)
|
||||
|
||||
|
|
@ -67,7 +69,16 @@ BENCH("Set state value derived", function()
|
|||
end
|
||||
end)
|
||||
|
||||
BENCH("Apply 4 properties", function()
|
||||
BENCH("Apply 0 properties", function()
|
||||
local apply = require "src/apply"
|
||||
local instance = vide.create("Frame") {}
|
||||
|
||||
for i = 1, START(N) do
|
||||
apply(instance, {})
|
||||
end
|
||||
end)
|
||||
|
||||
BENCH("Apply 8 properties", function()
|
||||
local apply = require "src/apply"
|
||||
local instance = vide.create("Frame") {}
|
||||
|
||||
|
|
@ -76,7 +87,11 @@ BENCH("Apply 4 properties", function()
|
|||
Name = i,
|
||||
Name2 = i,
|
||||
Name3 = i,
|
||||
Name4 = i
|
||||
Name4 = i,
|
||||
Name5 = i,
|
||||
Name6 = i,
|
||||
Name7 = i,
|
||||
Name8 = i,
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
|
@ -93,4 +108,99 @@ BENCH("Bind state", function()
|
|||
end
|
||||
end)
|
||||
|
||||
BENCH("Update binding", function()
|
||||
local apply = require "src/apply"
|
||||
local instance = vide.create("Frame") {}
|
||||
local state = vide.source(1)
|
||||
|
||||
apply(instance, {
|
||||
Name = state
|
||||
})
|
||||
|
||||
for i = 1, START(N) do
|
||||
state(i)
|
||||
end
|
||||
end)
|
||||
|
||||
BENCH("indexes() no change", function()
|
||||
local data = {}
|
||||
|
||||
for i = 1, N do
|
||||
data[i] = i
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
|
||||
local _list = vide.values(state, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
end)
|
||||
|
||||
BENCH("indexes() all change", function()
|
||||
local data = {}
|
||||
|
||||
for i = 1, N do
|
||||
data[i] = i
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
|
||||
local _list = vide.values(state, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
for i, v in data do
|
||||
data[i] = v + 1
|
||||
end
|
||||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
end)
|
||||
|
||||
BENCH("values() no change", function()
|
||||
local data = {}
|
||||
|
||||
for i = 1, N do
|
||||
data[i] = {}
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
|
||||
local _list = vide.values(state, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
end)
|
||||
|
||||
BENCH("values() all change", function()
|
||||
local data = {}
|
||||
|
||||
for i = 1, N do
|
||||
data[i] = {}
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
|
||||
local _list = vide.values(state, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
for i = 1, N do
|
||||
local r = math.random(1, #data)
|
||||
data[i], data[r] = data[r], data[i]
|
||||
end
|
||||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
end)
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -1054,15 +1054,33 @@ TEST("values()", function()
|
|||
local input = source { 1, 2, 3 }
|
||||
|
||||
local output = values(input, function(v, i)
|
||||
return v
|
||||
return { v = v, i = i }
|
||||
end)
|
||||
|
||||
input { 1, 2 }
|
||||
|
||||
local t = output()
|
||||
|
||||
CHECK(t[1] == 1)
|
||||
CHECK(t[2] == 2)
|
||||
CHECK(t[1].v == 1)
|
||||
CHECK(t[2].v == 2)
|
||||
CHECK(t[3] == nil)
|
||||
end
|
||||
|
||||
do CASE "Removal reflected 2"
|
||||
local input = source { 1 }
|
||||
|
||||
local output = values(input, function(v, i)
|
||||
return { v = v, i = i }
|
||||
end)
|
||||
|
||||
input { 2, 1 }
|
||||
input { 1 }
|
||||
|
||||
local t = output()
|
||||
|
||||
CHECK(t[1].v == 1)
|
||||
CHECK(t[1].i() == 1)
|
||||
CHECK(t[2] == nil)
|
||||
CHECK(t[3] == nil)
|
||||
end
|
||||
end)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue