From e6b029aefcbb2dcd09ab9e0b635be4defbcdbda6 Mon Sep 17 00:00:00 2001 From: Bogdan Popa Date: Mon, 6 Nov 2023 09:01:48 +0200 Subject: [PATCH] core,script: store chart entries in one struct instead of in parallel --- FranzCocoa/Backend.swift | 44 +++++++++++++++++++++--------- FranzCocoa/ResultDetail.swift | 9 +++---- FranzCross/result-detail.rkt | 11 ++++---- core/extlib/render.lua | 40 ++++++++------------------- core/script.rkt | 44 +++++++++++++++++++++--------- manual/index.scrbl | 51 +++++++++++++++++++---------------- 6 files changed, 112 insertions(+), 87 deletions(-) diff --git a/FranzCocoa/Backend.swift b/FranzCocoa/Backend.swift index 51ade91..f0d0fbf 100644 --- a/FranzCocoa/Backend.swift +++ b/FranzCocoa/Backend.swift @@ -534,51 +534,71 @@ public struct Broker: Readable, Writable { public struct Chart: Readable, Writable { public let style: ChartStyle + public let pairs: [ChartPair] public let xScale: ChartScale? public let xLabel: String - public let xs: [ChartValue] public let yScale: ChartScale? public let yLabel: String - public let ys: [ChartValue] public init( style: ChartStyle, + pairs: [ChartPair], xScale: ChartScale?, xLabel: String, - xs: [ChartValue], yScale: ChartScale?, - yLabel: String, - ys: [ChartValue] + yLabel: String ) { self.style = style + self.pairs = pairs self.xScale = xScale self.xLabel = xLabel - self.xs = xs self.yScale = yScale self.yLabel = yLabel - self.ys = ys } public static func read(from inp: InputPort, using buf: inout Data) -> Chart { return Chart( style: ChartStyle.read(from: inp, using: &buf), + pairs: [ChartPair].read(from: inp, using: &buf), xScale: ChartScale?.read(from: inp, using: &buf), xLabel: String.read(from: inp, using: &buf), - xs: [ChartValue].read(from: inp, using: &buf), yScale: ChartScale?.read(from: inp, using: &buf), - yLabel: String.read(from: inp, using: &buf), - ys: [ChartValue].read(from: inp, using: &buf) + yLabel: String.read(from: inp, using: &buf) ) } public func write(to out: OutputPort) { style.write(to: out) + pairs.write(to: out) xScale.write(to: out) xLabel.write(to: out) - xs.write(to: out) yScale.write(to: out) yLabel.write(to: out) - ys.write(to: out) + } +} + +public struct ChartPair: Readable, Writable { + public let x: ChartValue + public let y: ChartValue + + public init( + x: ChartValue, + y: ChartValue + ) { + self.x = x + self.y = y + } + + public static func read(from inp: InputPort, using buf: inout Data) -> ChartPair { + return ChartPair( + x: ChartValue.read(from: inp, using: &buf), + y: ChartValue.read(from: inp, using: &buf) + ) + } + + public func write(to out: OutputPort) { + x.write(to: out) + y.write(to: out) } } diff --git a/FranzCocoa/ResultDetail.swift b/FranzCocoa/ResultDetail.swift index 347ffc2..3a4d6e0 100644 --- a/FranzCocoa/ResultDetail.swift +++ b/FranzCocoa/ResultDetail.swift @@ -57,8 +57,7 @@ fileprivate struct ChartResult: View { } private func chartView() -> any View { - let ps = pairs(chart.xs, chart.ys) - let view = Charts.Chart(ps) { p in + let view = Charts.Chart(pairs) { p in switch chart.style { case .bar: try? p.barMark(xLabel: chart.xLabel, yLabel: chart.yLabel) @@ -178,10 +177,10 @@ fileprivate struct ChartResult: View { } } - private func pairs(_ xs: [ChartValue], _ ys: [ChartValue]) -> [Pair] { + private var pairs: [Pair] { var pairs = [Pair]() - for (i, (x, y)) in zip(xs, ys).enumerated() { - pairs.append(.init(id: i, x: x, y: y)) + for (i, p) in chart.pairs.enumerated() { + pairs.append(.init(id: i, x: p.x, y: p.y)) } return pairs } diff --git a/FranzCross/result-detail.rkt b/FranzCross/result-detail.rkt index ae39f3d..b02c44b 100644 --- a/FranzCross/result-detail.rkt +++ b/FranzCross/result-detail.rkt @@ -52,9 +52,9 @@ (define-values (y-min y-max) (ChartScale-> (Chart-y-scale c))) (define x-date-ticks? - (ormap ChartValue.timestamp? (Chart-xs c))) + (ormap (compose1 ChartValue.timestamp? ChartPair-x) (Chart-pairs c))) (define y-date-ticks? - (ormap ChartValue.timestamp? (Chart-ys c))) + (ormap (compose1 ChartValue.timestamp? ChartPair-y) (Chart-pairs c))) (parameterize ([candlestick-width (match (Chart-style c) [(ChartStyle.candlestick width) @@ -84,13 +84,12 @@ [(ChartStyle.candlestick _) candlesticks] [(ChartStyle.line) lines] [(ChartStyle.scatter) points]) - (for/list ([x (in-list (Chart-xs c))] - [y (in-list (Chart-ys c))]) + (for/list ([p (in-list (Chart-pairs c))]) ((match (Chart-style c) [(ChartStyle.candlestick _) list*] [_ list]) - (ChartValue-> x) - (ChartValue-> y))))))))))) + (ChartValue-> (ChartPair-x p)) + (ChartValue-> (ChartPair-y p)))))))))))) (define (text-view s) (hpanel diff --git a/core/extlib/render.lua b/core/extlib/render.lua index d8288d8..4cf1003 100644 --- a/core/extlib/render.lua +++ b/core/extlib/render.lua @@ -22,21 +22,18 @@ local function makeChartClass(name) __type = name, xlabel = xlabel or "x axis", ylabel = ylabel or "y axis", - xs = {}, - ys = {}, + values = {}, } end } function Chart:clear() - self.xs = {} - self.ys = {} + self.values = {} return self end function Chart:push(x, y) - table.insert(self.xs, x) - table.insert(self.ys, y) + table.insert(self.values, { x = x; y = y }) return self end @@ -49,6 +46,8 @@ local function makeChartClass(name) end local function make_scale(who, lo, hi, typ) + check(who, lo, "number", 1) + check(who, hi, "number", 2) check_type(who, typ) return { lo = lo, @@ -57,14 +56,14 @@ local function makeChartClass(name) } end - function Chart:setxscale(...) - check_type("Chart:setxscale", typ) - self.xscale = make_scale("Chart:setxscale", ...) + function Chart:setvalues(...) + self.values = {...} return self end - function Chart:setxs(xs) - self.xs = xs + function Chart:setxscale(...) + check_type("Chart:setxscale", typ) + self.xscale = make_scale("Chart:setxscale", ...) return self end @@ -74,28 +73,11 @@ local function makeChartClass(name) return self end - function Chart:setys(ys) - self.ys = ys - return self - end - function Chart:sort(cmp) cmp = cmp or function(a, b) return a.x < b.x end - local ps = {} - for i, x in ipairs(self.xs) do - table.insert(ps, {x = x, y = self.ys[i] }) - end - table.sort(ps, cmp) - local xs = {} - local ys = {} - for i, p in ipairs(ps) do - xs[i] = p.x - ys[i] = p.y - end - self.xs = xs - self.ys = ys + table.sort(self.values, cmp) return self end diff --git a/core/script.rkt b/core/script.rkt index 3d2482f..c0f0f1f 100644 --- a/core/script.rkt +++ b/core/script.rkt @@ -1,6 +1,8 @@ #lang racket/base -(require avro +(require (for-syntax racket/base + syntax/parse) + avro kafka/private/serde/internal lua/env lua/private/string ;; FIXME @@ -21,6 +23,7 @@ (provide (record-out ApplyResult) (record-out Chart) + (record-out ChartPair) (record-out Stack) (record-out TableRow) (enum-out ChartScale) @@ -59,14 +62,17 @@ [numerical {n : Float64}] [timestamp {t : UVarint}]) +(define-record ChartPair + [x : ChartValue] + [y : ChartValue]) + (define-record Chart [style : ChartStyle] + [pairs : (Listof ChartPair)] [x-scale : (Optional ChartScale)] [x-label : String] - [xs : (Listof ChartValue)] [y-scale : (Optional ChartScale)] - [y-label : String] - [ys : (Listof ChartValue)]) + [y-label : String]) (define-enum ReduceResult [chart {c : Chart}] @@ -137,6 +143,11 @@ (error '->ChartValue "invalid table: ~s" v)])] [else (error '->ChartValue "invalid value: ~s" v)])) +(define (->ChartPair t) + (make-ChartPair + #:x (->ChartValue (table-ref t #"x")) + #:y (->ChartValue (table-ref t #"y")))) + (define (->ReduceResult v) (cond [(not v) #f] @@ -151,12 +162,11 @@ (ReduceResult.chart (make-Chart #:style (->ChartStyle type v) + #:pairs (table->list (table-ref v #"values") ->ChartPair) #:x-scale (->ChartScale (table-ref v #"xscale")) #:x-label (table-ref-string v #"xlabel") - #:xs (table->list (table-ref v #"xs") ->ChartValue) #:y-scale (->ChartScale (table-ref v #"yscale")) - #:y-label (table-ref-string v #"ylabel") - #:ys (table->list (table-ref v #"ys") ->ChartValue)))] + #:y-label (table-ref-string v #"ylabel")))] [(#"HStack" #"VStack") (ReduceResult.stack (make-Stack @@ -357,11 +367,21 @@ SCRIPT ;; extlib ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define-runtime-module-path-index avro.lua "extlib/avro.lua") -(define-runtime-module-path-index class.lua "extlib/class.lua") -(define-runtime-module-path-index kafka.lua "extlib/kafka.lua") -(define-runtime-module-path-index msgpack.lua "extlib/msgpack.lua") -(define-runtime-module-path-index render.lua "extlib/render.lua") +(define-syntax (defmod stx) + (syntax-parse stx + [(_ name:id) + #:with path (datum->syntax #'name (format "extlib/~a" (syntax->datum #'name))) + #'(define-runtime-module-path-index name path)])) + +(define-syntax-rule (defmods name ...) + (begin (defmod name) ...)) + +(defmods + avro.lua + class.lua + kafka.lua + msgpack.lua + render.lua) (define symbol->bytes (compose1 string->bytes/utf-8 symbol->string)) diff --git a/manual/index.scrbl b/manual/index.scrbl index e1825c1..5d4df75 100644 --- a/manual/index.scrbl +++ b/manual/index.scrbl @@ -887,12 +887,19 @@ aggregated data to a window when applying a script. @codeblock[#:keep-lang-line? #f]{ #lang lua + -- Assuming `state' looks like: + -- { + -- n = number, + -- xs = {...}, + -- ys = {...} + -- } function script.render(state) local total = 0 - for _, age in ipairs(state.ys) do + local pairs = {} + for i, age in ipairs(state.ys) do total = total + age + pairs[i] = { x = state.xs[i]; y = age } end - local avg = total / state.n return render.VStack( render.HStack( string.format("Total Records: %d", state.n), @@ -901,15 +908,14 @@ aggregated data to a window when applying a script. { math.min(table.unpack(state.ys)), math.max(table.unpack(state.ys)), - avg + (total / state.n) } ) ), render.BarChart("Name", "Age") - :setxs(state.xs) - :setys(state.ys) - :sort() + :setvalues(table.unpack(pairs)) :setyscale(0, 105) + :sort() ) end } @@ -936,28 +942,26 @@ See @secref["rendering-a-bar-chart"] for a usage example. numbers, but the types should be internally consistent per axis. } +@deflua[Chart:setvalues (...) Chart]{ + Replaces all the chart values with @tt{...}. Each individual value + must be a table with @tt{x} and @tt{y} fields. +} + @deflua[Chart:setxscale (lo hi) Chart]{ Sets the scale of the x axis. Undefined when the x values are strings. } -@deflua[Chart:setxs (xs) Chart]{ - Replaces all the x-axis values with @tt{xs}. -} - @deflua[Chart:setyscale (lo hi) Chart]{ Sets the scale of the y axis. Undefined when the y values are strings. } -@deflua[Chart:setys (ys) Chart]{ - Replaces all the y-axis values with @tt{ys}. -} - @deflua[Chart:sort (cmp) Chart]{ Sorts the data contained by the chart according to @tt{cmp}. If not - provided, @tt{cmp} defaults to @tt{<}. The arguments to @tt{cmp} are - two tables with one field for the @tt{x} and @tt{y} values each. + provided, @tt{cmp} defaults to comparing the @tt{x} values using the + @tt{<} operator. The arguments to @tt{cmp} are two tables with one + field for the @tt{x} and @tt{y} values each. } @@ -1079,9 +1083,11 @@ data. end function script.reduce(record, state) - state = state or { xs = {}; ys = {} } - table.insert(state.xs, tostring(record.offset)) - table.insert(state.ys, #record.value) + state = state or { values = {} } + table.insert(state.values, { + x = tostring(record.offset), + y = #record.value + }) return state end @@ -1089,10 +1095,9 @@ data. local function cmp(a, b) return a.x < b.x end - local chart = render.BarChart("offset", "length") - chart:setxs(state.xs) - chart:setys(state.ys) - return chart:sort(cmp) + return render.BarChart("offset", "length") + :setvalues(table.unpack(state.values)) + :sort(cmp) end return script