×
¥
查看详情
🔥 会员专享 文生文 开发

个人理财编程语言文档生成器

👁️ 193 次查看
📅 Nov 16, 2025
💡 核心价值: 本提示词专为个人理财编程语言开发者设计,能够高效生成结构清晰、内容详实的使用文档和示例教程。它通过系统化的工作流程,确保文档涵盖语言功能说明、实际应用示例和操作指南,帮助用户快速掌握语言特性并应用于实际开发中。亮点包括多维度功能解析、可运行代码示例生成以及灵活的文档格式适配,显著提升开发效率和文档质量。

🎯 可自定义参数(4个)

功能列表
需要文档化的语言功能清单
文档格式
最终文档的输出格式
目标用户群体
文档的主要使用人群
语言复杂度级别
语言功能的复杂程度

🎨 效果示例

语言概述

FinLite 是一门面向初学者的个人理财编程语言。它以“可读、可运行、可复用”为设计理念,帮助你用简单语句完成预算管理、交易导入与校验、分类规则、基础报表等常用任务。
核心优势:

  • 语法直观:接近自然语言的语句,降低上手门槛
  • 类型安全:内置日期与金额类型,减少格式错误
  • 一体化流程:导入、校验、预算、报表在一处完成,便于复盘
  • 可执行示例:文档中的代码均可直接运行,快速获得结果

适用场景:家庭预算、日常记账、收入支出汇总、现金流趋势洞察。
说明:本文档仅介绍语言用法,不提供任何财务建议或投资指导。


功能详解

1. 语法概览:变量、账户、分类

  • 注释:以 # 开头
  • 变量:let 变量名 = 值
  • 项目声明:project "项目名" baseCurrency "ISO币种代码"
  • 账户:account "账户全名" type <asset|liability|income|expense> currency "ISO币种" [open "YYYY-MM-DD"]
  • 分类:category "分类全名" [parent "父分类"]

示例:

# 项目与基础变量
project "家庭账本" baseCurrency "CNY"
let owner = "Lee 家庭"
let startMonth = month("2025-01")

# 账户定义(资产/收入/支出)
account "Bank:Checking"  type asset  currency "CNY" open "2024-12-01"
account "Income:Salary"  type income currency "CNY"
account "Expense:Rent"   type expense currency "CNY"
account "Expense:Groceries" type expense currency "CNY"

# 分类定义(用于预算与报表汇总)
category "Housing:Rent"        parent "Housing"
category "Food:Groceries"      parent "Food"
category "Income:Salary"       parent "Income"

命名建议:

  • 账户使用“组:名称”格式(例:Bank:Checking)
  • 分类使用“领域:条目”格式(例:Food:Groceries)

2. 日期与金额类型

  • 日期:date("YYYY-MM-DD")
  • 月份:month("YYYY-MM")
  • 金额:money("ISO币种", 数值)

示例:

let d1 = date("2025-01-15")
let m1 = month("2025-02")
let amt = money("CNY", 1200.00)

注意:

  • 金额必须使用 ISO 币种代码(如 "CNY")
  • 字符串日期必须为 YYYY-MM-DD,月份为 YYYY-MM

3. 预算语句:月度/年度/滚动预算

语法:

  • 月度预算:budget monthly "分类或账户" 金额 from 月份 to 月份 [rollover true|false]
  • 年度预算:budget annual "分类或账户" 金额 for YYYY [rollover true|false]
  • 滚动预算:budget rolling "分类或账户" 金额 start 月份 months 数字 rollover true

示例:

# 三个月的食品预算
budget monthly "Food:Groceries" money("CNY", 1500.00) from month("2025-01") to month("2025-03") rollover false

# 年度房租预算
budget annual "Housing:Rent" money("CNY", 36000.00) for 2025 rollover false

# 滚动杂项预算(每月结余结转)
budget rolling "Leisure:Misc" money("CNY", 500.00) start month("2025-01") months 12 rollover true

说明:

  • rollover 为 true 时,未用完的预算会结转到下个周期
  • “分类或账户”通常使用分类名称;如需对某账户单独预算也可使用账户名

4. 交易处理:导入 CSV、基本校验、重复检测

导入 CSV:

import csv "data/transactions.csv"
  into ledger
  map { date: "Date", payee: "Payee", amount: "Amount", currency: "Currency", memo: "Memo" }
  defaultAccount "Bank:Checking"
  dateFormat "YYYY-MM-DD"

基本校验:

validate basic
# 校验项:日期格式、金额为数值、币种合法、缺列检查

重复检测:

detect duplicates
  key [date, amount, payee]
  within days 60
  action mark  # 标记并跳过重复项(不入账)

自动记账规则(分类与备注增强):

rule when payee contains "公司发薪"
  then category "Income:Salary" memo "工资入账"

rule when payee contains "房租"
  then category "Housing:Rent" memo "月度房租"

rule when payee contains "超市"
  then category "Food:Groceries" memo "家庭食品采购"

CSV 示例(保存为 data/transactions.csv):

Date,Payee,Amount,Currency,Memo
2025-01-05,公司发薪,12000.00,CNY,一月工资
2025-01-06,超市购物,-220.80,CNY,家庭食品
2025-01-10,房租,-3000.00,CNY,一月房租
2025-02-05,公司发薪,12000.00,CNY,二月工资
2025-02-08,超市购物,-250.50,CNY,家庭食品
2025-02-10,房租,-3000.00,CNY,二月房租
2025-03-05,公司发薪,12000.00,CNY,三月工资
2025-03-07,超市购物,-200.00,CNY,家庭食品
2025-03-10,房租,-3000.00,CNY,三月房租
# 以下为可能重复的示例(与 2025-01-06 金额与商户一致)
2025-01-06,超市购物,-220.80,CNY,重复记录示例

5. 简单报表:收支汇总、现金流趋势

收支汇总:

report summary
  period month("2025-01")..month("2025-03")
  group by category
  currency "CNY"

现金流趋势:

report cashflow
  period month("2025-01")..month("2025-03")
  interval month
  accounts ["Bank:Checking"]
  currency "CNY"

结果说明:

  • 汇总会显示分类下的收入与支出合计(重复记录已跳过)
  • 现金流趋势按月显示净流入(收入减支出)

6. 错误提示与常见坑

常见错误与提示:

  • E1001 InvalidDateFormat:日期需为 "YYYY-MM-DD"。修正示例:date("2025-02-01")
  • E1002 UnknownCurrency:币种必须为 ISO 代码(如 CNY、USD)。修正示例:money("CNY", 100.00)
  • E2001 UnknownAccountOrCategory:规则或预算引用的名称未定义。先定义 accountcategory
  • E3001 MissingCSVColumn:导入映射缺少必须列。检查 map { date, payee, amount, currency }
  • W4001 DuplicateDetected:检测到重复交易,已标记并跳过。处理方式:确认 CSV 数据源是否重复导出

常见坑与避免:

  • 忘记设置 defaultAccount 导致导入数据没有账户归属
  • rollover true 会造成结余累积,若不需要请设为 false
  • 规则匹配为“包含”而非“完全等于”,注意关键词选择(如“超市” vs “超市购物”)
  • 月份区间使用 month("YYYY-MM")..month("YYYY-MM"),不要混用日期与月份类型

7. 快速开始:项目结构与常用命令

推荐项目结构:

my-ledger/
  main.fl               # 主入口(项目声明、导入、规则、报表)
  accounts.fl           # 账户定义
  categories.fl         # 分类定义
  budgets.fl            # 预算定义
  reports.fl            # 自定义报表(可选)
  data/
    transactions.csv    # 原始交易数据

常用命令(FinLite CLI):

  • 初始化:fl init my-ledger
  • 运行全部脚本:fl run(默认加载当前目录下所有 .fl 文件)
  • 单文件运行:fl run main.fl
  • 导入数据并校验:fl import data/transactions.csv && fl validate
  • 生成汇总报表:fl report summary --period 2025-01..2025-03 --currency CNY
  • 生成现金流趋势:fl report cashflow --period 2025-01..2025-03 --interval month --account "Bank:Checking"

说明:

  • 若使用多文件,fl run 会按文件名顺序加载;确保 projectaccount/category 在使用前定义
  • 命令参数与脚本语句一一对应,便于脚本与命令行互换

示例教程

以下示例均可直接运行。请在空目录中创建文件与数据后执行。

入门示例(Level 1):最小可运行账本

文件:main.fl

# 1) 项目与基础
project "家庭账本(入门)" baseCurrency "CNY"

# 2) 账户与分类
account "Bank:Checking"        type asset  currency "CNY"
category "Income:Salary"       parent "Income"
category "Food:Groceries"      parent "Food"

# 3) 导入与校验
import csv "data/transactions.csv"
  into ledger
  map { date: "Date", payee: "Payee", amount: "Amount", currency: "Currency", memo: "Memo" }
  defaultAccount "Bank:Checking"
  dateFormat "YYYY-MM-DD"

validate basic
detect duplicates key [date, amount, payee] within days 60 action mark

# 4) 简单规则(可选)
rule when payee contains "公司发薪" then category "Income:Salary"
rule when payee contains "超市"     then category "Food:Groceries"

数据:data/transactions.csv(参见上文 CSV 示例)

运行步骤与结果说明:

  • 执行:fl run
  • 预期:导入 10 行,重复检测跳过 1 行(W4001),有效入账 9 行

进阶示例(Level 2):三个月家庭预算 + 汇总报表

文件:budgets.fl

project "家庭账本(预算)" baseCurrency "CNY"

# 账户与分类(供预算与报表使用)
account "Bank:Checking"        type asset  currency "CNY"
category "Housing:Rent"        parent "Housing"
category "Food:Groceries"      parent "Food"
category "Income:Salary"       parent "Income"

# 预算
budget monthly "Food:Groceries" money("CNY", 1500.00) from month("2025-01") to month("2025-03") rollover false
budget annual  "Housing:Rent"   money("CNY", 36000.00) for 2025 rollover false

# 导入与规则(为计算预算执行情况)
import csv "data/transactions.csv"
  into ledger
  map { date: "Date", payee: "Payee", amount: "Amount", currency: "Currency", memo: "Memo" }
  defaultAccount "Bank:Checking"
  dateFormat "YYYY-MM-DD"

validate basic
detect duplicates key [date, amount, payee] within days 60 action mark

rule when payee contains "公司发薪" then category "Income:Salary"
rule when payee contains "房租"     then category "Housing:Rent"
rule when payee contains "超市"     then category "Food:Groceries"

# 报表:收支汇总(2025-01 至 2025-03)
report summary
  period month("2025-01")..month("2025-03")
  group by category
  currency "CNY"

运行与结果说明:

  • 执行:fl run budgets.fl
  • 预期汇总(重复已跳过):
    • Income:Salary 合计 +36,000.00 CNY
    • Housing:Rent 合计 -9,000.00 CNY
    • Food:Groceries 合计 -671.30 CNY
  • 预算对比(示意):
    • 食品预算:每月 1,500;实际(1-3月)总计 671.30,均在预算内
    • 房租年度预算:36,000;实际(1-3月)9,000;剩余 27,000(年度内)

高阶示例(Level 3):自动记账规则 + 现金流趋势

文件:rules.fl

project "家庭账本(规则与趋势)" baseCurrency "CNY"

account "Bank:Checking"        type asset  currency "CNY"
category "Income:Salary"       parent "Income"
category "Housing:Rent"        parent "Housing"
category "Food:Groceries"      parent "Food"

import csv "data/transactions.csv"
  into ledger
  map { date: "Date", payee: "Payee", amount: "Amount", currency: "Currency", memo: "Memo" }
  defaultAccount "Bank:Checking"
  dateFormat "YYYY-MM-DD"

validate basic
detect duplicates key [date, amount, payee] within days 60 action mark

# 自动记账规则(增强备注)
rule when payee contains "公司发薪" then category "Income:Salary" memo "工资入账"
rule when payee contains "房租"     then category "Housing:Rent" memo "房租支付"
rule when payee contains "超市"     then category "Food:Groceries" memo "食品采购"

# 现金流趋势(按月)
report cashflow
  period month("2025-01")..month("2025-03")
  interval month
  accounts ["Bank:Checking"]
  currency "CNY"

运行与结果说明:

  • 执行:fl run rules.fl
  • 预期现金流净额:
    • 2025-01:+8,779.20 CNY
    • 2025-02:+8,749.50 CNY
    • 2025-03:+8,800.00 CNY
  • 规则生效后,交易备注会被统一与补充,便于后续检索

最佳实践

  • 文件组织:
    • 将账户、分类、预算、规则分别放入独立 .fl 文件,主入口统一导入数据与生成报表
  • 命名一致:
    • 分类与账户使用清晰分层(如 Housing:Rent、Food:Groceries),便于汇总
  • CSV 清洗:
    • 保持统一日期格式与币种代码;导入前尽量去重以缩短处理时间
  • 预算管理:
    • 月度预算适用于消费类;年度预算适用于固定支出;滚动预算用于灵活消费
  • 规则编写:
    • 使用“包含”关键词匹配常见商户名;避免过宽(如“店”)或过窄(如全名可能变动)
  • 校验先行:
    • validate 再报表;确保错误数据不影响统计
  • 安全与隐私:
    • 示例数据为虚构;实际使用时避免在脚本中保存真实敏感信息(如账号、身份证号)

附录

快速参考表(语句与参数)

  • project
    • 用法:project "名称" baseCurrency "ISO"
  • let
    • 用法:let 名称 = 值(支持字符串、日期、月份、金额)
  • account
    • 参数:名称、type(asset/liability/income/expense)、currency、open(可选)
  • category
    • 参数:名称、parent(可选)
  • money / date / month
    • 用法:money("CNY", 100.00)date("2025-01-01")month("2025-01")
  • budget monthly / annual / rolling
    • 关键参数:目标名、金额、周期(from/to、for、start/months)、rollover
  • import csv
    • 参数:路径、映射 map { date, payee, amount, currency, memo }defaultAccountdateFormat
  • validate basic
    • 功能:日期/金额/币种/缺列检查
  • detect duplicates
    • 参数:key(字段数组)、within days(窗口期)、action(mark)
  • rule
    • 用法:rule when payee contains "关键词" then category "分类" memo "备注"(memo 可选)
  • report summary
    • 参数:period(月份区间)、group by(category/account)、currency
  • report cashflow
    • 参数:period、interval(month/week)、accounts(数组)、currency

示例数据列说明(CSV)

  • Date:YYYY-MM-DD
  • Payee:商户或来源
  • Amount:正数为收入,负数为支出
  • Currency:ISO 币种代码(如 CNY)
  • Memo:备注(可选)

资源链接

  • 语言主页与下载:建议访问官方站点获取 CLI 与编辑器扩展(示例占位:请搜索“FinLite CLI”)
  • 社区与教程:可查找“FinLite 教程与问答”获取更多入门示例
  • ISO 币种代码参考:搜索“ISO 4217 currency codes”

说明:以上链接为通用指引,请根据实际发布渠道选择官方来源。本文档示例不包含任何敏感财务数据与建议。

语言概述

FinDSL(Finance Domain-Specific Language)是一门面向个人理财的领域专用编程语言,强调数据安全、计算可重复与语义清晰。它为预算管理、投资持仓追踪、税费规则表达、数据适配与批处理调度等常见个人理财任务提供内置能力,并通过模块化与类型系统保障代码的可维护性与可验证性。

核心优势:

  • 清晰的类型系统与校验器,降低财务数据处理中的出错率
  • 一体化数据适配器(CSV/JSON/数据库),简化数据导入与转换
  • 面向投资组合的持仓模型与再平衡策略工具,支持可视化的假设计算
  • 税费规则表达与可组合的分级税率模型,适用于测算与模拟(不包含投资建议)
  • 计划任务与调度支持,便于构建每日批处理流程
  • 内置测试框架与打包发布工具,提升工程化质量与交付一致性
  • 直观的日志与异常处理机制,便于审计与问题定位

注意:本文档仅用于技术说明与示例演示,所有税费与投资计算均为通用模型示例,不构成财务建议或投资指导。


功能详解

1. 基本语法与类型系统

内置基本类型:

  • Int、Decimal、String、Bool、Date(ISO 格式:YYYY-MM-DD)
  • Money(货币金额):Money(currency: String, amount: Decimal)
  • Percentage(百分比,0.0–1.0):percent(0.10) 表示 10%
  • Symbol(资产代码,字符串类型)
  • 集合类型:List[T]、Map[K, V]

示例:

// 注释:使用 money() 构造金额,使用 date() 构造日期
let start: Date = date("2025-01-01")
let budget: Money = money("CNY", 2000.00)
let rate: Percentage = percent(0.05) // 5%

类型定义与复合结构:

type Transaction {
  date: Date,
  category: String,
  amount: Money
}

type Holding {
  symbol: Symbol,
  quantity: Decimal,
  price: Money // 市场价格(单位价格)
}

2. 校验器(Validators)

校验器用于在赋值或函数入参阶段进行约束检查。支持简单布尔表达式与组合规则。

定义与应用:

validator PositiveMoney(m: Money) {
  rule m.amount > 0
}

validator NonEmptyText(s: String) {
  rule len(s) > 0
}

// 应用在变量声明
let rent: Money @PositiveMoney = money("CNY", 1800.00)

// 应用在函数参数(入参自动校验)
fn safe_add(a: Money @PositiveMoney, b: Money @PositiveMoney): Money {
  return money(a.currency, a.amount + b.amount)
}

内置校验器(示例):

  • NonNegativeDecimal
  • ValidDate
  • InRangeDecimal(min: Decimal, max: Decimal)
  • CurrencyEquals(code: String)

使用组合:

validator RentRules(m: Money) {
  rule m.amount > 0 && m.currency == "CNY"
}

3. 函数与模块

模块用于组织可复用函数。默认文件为一个模块,显式模块写法如下:

module budgets {
  export fn total_by_category(txns: List[Transaction]): Map[String, Money] {
    let totals: Map[String, Money] = {}
    for t in txns {
      let cur: Money = totals.get(t.category, money(t.amount.currency, 0))
      totals[t.category] = money(cur.currency, cur.amount + t.amount.amount)
    }
    return totals
  }

  export fn monthly_total(txns: List[Transaction], month: String): Money {
    // month 格式:YYYY-MM
    let sum: Decimal = 0
    for t in txns {
      if substr(str(t.date), 0, 7) == month {
        sum = sum + t.amount.amount
      }
    }
    return money("CNY", sum)
  }
}

// 在其他文件中使用
use budgets

let m = budgets.monthly_total(my_txns, "2025-01")

函数签名:

  • 语法:fn name(args...): ReturnType { ... }
  • 参数可带类型与校验器
  • return 返回值必须匹配 ReturnType

4. 异常处理与日志

异常:

try {
  let conn = db.connect("sqlite://:memory:")
  // ...
} catch e: AdapterError {
  log.error("DB 连接失败: " + e.message)
} catch e: ValidationError {
  log.warn("校验未通过: " + e.message)
}

日志级别:

  • log.trace(msg)
  • log.debug(msg)
  • log.info(msg)
  • log.warn(msg)
  • log.error(msg)

示例:

log.info("开始导入 CSV")

5. 数据适配器:CSV / JSON / 数据库连接

导入模块:

import adapters.csv as csv
import adapters.json as json
import adapters.db as db

CSV 读取:

let rows = csv.read("data/expenses.csv") // List[Map[String, String]]
// 映射到 Transaction
fn map_tx(row: Map[String, String]): Transaction {
  return Transaction {
    date: date(row["date"]),
    category: row["category"],
    amount: money("CNY", parse_decimal(row["amount"]))
  }
}
let txns: List[Transaction] = rows.map(map_tx)

JSON 读取:

let data = json.read("data/holdings.json") // Map 或 List
// 假设结构:[{ "symbol": "ETF.A", "quantity": 10, "price": 5.2 }]
fn map_h(row: Map[String, Any]): Holding {
  return Holding {
    symbol: Symbol(row["symbol"]),
    quantity: parse_decimal(str(row["quantity"])),
    price: money("CNY", parse_decimal(str(row["price"])))
  }
}
let holds: List[Holding] = data.map(map_h)

数据库(SQLite 示例,安全本地):

let conn = db.connect("sqlite://./fins_demo.db")
db.exec(conn, "CREATE TABLE IF NOT EXISTS tx (date TEXT, category TEXT, amount REAL)")
db.exec(conn, "INSERT INTO tx (date,category,amount) VALUES ('2025-01-02','Groceries',120.5)")
let rows = db.query(conn, "SELECT date, category, amount FROM tx")
let txns = rows.map(fn (r) {
  return Transaction {
    date: date(r["date"]),
    category: r["category"],
    amount: money("CNY", parse_decimal(str(r["amount"])))
  }
})
db.close(conn)

6. 投资组合:持仓模型与再平衡策略

持仓与市值:

type Portfolio {
  holdings: List[Holding],
  cash: Money
}

fn market_value(h: Holding): Money {
  return money(h.price.currency, h.price.amount * h.quantity)
}

fn portfolio_value(p: Portfolio): Money {
  let total: Decimal = p.holdings.reduce(0, fn (acc, h) { acc + market_value(h).amount }) + p.cash.amount
  return money(p.cash.currency, total)
}

目标权重与再平衡(假设计算,非交易建议):

type Order { side: String, symbol: Symbol, quantity: Decimal }

fn rebalance(p: Portfolio, targets: Map[Symbol, Percentage], drift: Percentage): List[Order] {
  // 计算当前权重
  let total = portfolio_value(p).amount
  let current_weights: Map[Symbol, Percentage] = {}
  for h in p.holdings {
    let w = market_value(h).amount / total
    current_weights[h.symbol] = percent(w)
  }

  // 生成订单(当偏离超过 drift 时)
  let orders: List[Order] = []
  for symbol, target in targets {
    let cur = current_weights.get(symbol, percent(0))
    let delta = target.value - cur.value
    if abs(delta) >= drift.value {
      // 目标金额与数量(简单线性近似)
      let target_amount = total * target.value
      // 查找持仓与价格
      let h = p.holdings.find(fn (x) { x.symbol == symbol })
      let price = (h == null) ? money(p.cash.currency, 0) : h.price
      if price.amount > 0 {
        let desired_qty = target_amount / price.amount
        let current_qty = (h == null) ? 0 : h.quantity
        let diff = desired_qty - current_qty
        if diff > 0 {
          orders.push(Order { side: "BUY", symbol: symbol, quantity: diff })
        } else if diff < 0 {
          orders.push(Order { side: "SELL", symbol: symbol, quantity: abs(diff) })
        }
      }
    }
  }
  return orders
}

说明:

  • drift 为触发再平衡的最小偏离阈值(如 5%)
  • 生成的 Order 为假设订单,仅用于计算与演示,不构成指令或建议

7. 税费规则表达:资本利得与分级税率

税费规则模型(示例,不代表实际税率):

type TaxBracket { up_to: Money, rate: Percentage }
type TaxScheme { brackets: List[TaxBracket], default_rate: Percentage }

fn calc_tiered_tax(gain: Money, scheme: TaxScheme): Money {
  let remaining = gain.amount
  let tax: Decimal = 0
  for b in scheme.brackets {
    let cap = b.up_to.amount
    let portion = min(remaining, cap)
    tax = tax + portion * b.rate.value
    remaining = remaining - portion
    if remaining <= 0 { break }
  }
  if remaining > 0 {
    tax = tax + remaining * scheme.default_rate.value
  }
  return money(gain.currency, tax)
}

fn capital_gains_tax(gain: Money, days_held: Int, long_term: TaxScheme, short_rate: Percentage): Money {
  if days_held >= 365 {
    return calc_tiered_tax(gain, long_term)
  } else {
    return money(gain.currency, gain.amount * short_rate.value)
  }
}

示例方案(演示用):

let long_term_scheme = TaxScheme {
  brackets: [
    TaxBracket { up_to: money("CNY", 10000), rate: percent(0.05) },
    TaxBracket { up_to: money("CNY", 30000), rate: percent(0.10) }
  ],
  default_rate: percent(0.15)
}

let short_rate = percent(0.25)

// 计算示例
let gain = money("CNY", 42000)
let tax_long = capital_gains_tax(gain, 400, long_term_scheme, short_rate)
let tax_short = capital_gains_tax(gain, 100, long_term_scheme, short_rate)

说明:

  • 以上税率仅为演示参数,请根据当地法规自行配置
  • 函数用于静态测算,不构成实务申报建议

8. 计划任务:按日批处理与调度

定义每日任务:

import scheduler

task import_daily_expenses {
  log.info("开始每日导入")
  let rows = csv.read("data/expenses.csv")
  let txns = rows.map(map_tx)
  let total = budgets.monthly_total(txns, substr(str(date_now()), 0, 7))
  log.info("本月累计支出: " + str(total.amount) + " " + total.currency)
}

schedule daily at "09:00" run import_daily_expenses

说明:

  • task 定义可执行任务单元
  • schedule 支持 daily / weekly / cron("0 9 * * *") 等形式
  • 运行时通过 CLI 调度守护或一次性执行

9. 测试框架:单元与集成测试

单元测试:

test "monthly_total basic" {
  let txns = [
    Transaction { date: date("2025-01-02"), category: "Food", amount: money("CNY", 50) },
    Transaction { date: date("2025-01-11"), category: "Transport", amount: money("CNY", 30) },
    Transaction { date: date("2025-02-01"), category: "Food", amount: money("CNY", 20) }
  ]
  let jan = budgets.monthly_total(txns, "2025-01")
  assert eq(jan.amount, 80)
}

test "capital_gains_tax long_term tiered" {
  let gain = money("CNY", 42000)
  let long_term_scheme = TaxScheme {
    brackets: [
      TaxBracket { up_to: money("CNY", 10000), rate: percent(0.05) },
      TaxBracket { up_to: money("CNY", 30000), rate: percent(0.10) }
    ],
    default_rate: percent(0.15)
  }
  let tax = capital_gains_tax(gain, 400, long_term_scheme, percent(0.25))
  // 计算期望:10000*5% + 30000*10% + 2000*15% = 500 + 3000 + 300 = 3800
  assert eq(tax.amount, 3800)
}

集成测试(适配器与模块协作):

test "csv -> budgets integration" {
  let rows = [
    {"date": "2025-01-01", "category": "Rent", "amount": "1800"},
    {"date": "2025-01-02", "category": "Food", "amount": "50"}
  ]
  // 模拟 csv.read 的返回
  let txns = rows.map(map_tx)
  let totals = budgets.total_by_category(txns)
  assert eq(totals["Rent"].amount, 1800)
  assert eq(totals["Food"].amount, 50)
}

10. 打包发布与 CLI 操作示例

项目结构(推荐):

  • src/…:源码与模块
  • tests/…:测试用例
  • data/…:示例数据
  • fins.toml:项目配置(名称、版本、依赖等)

CLI 基本命令:

  • fins run path/to/main.fins:运行脚本或任务
  • fins test:运行测试套件
  • fins fmt:格式化代码
  • fins package build:构建可分发包
  • fins package publish:发布包(本地/私有仓库)
  • fins schedule start:启动调度守护

示例:

# 运行主脚本
fins run src/main.fins

# 执行测试
fins test

# 格式化并构建
fins fmt
fins package build

示例教程

入门级:预算汇总与分类

使用场景:

  • 从 CSV 导入月度支出,按分类汇总并输出本月总额

代码示例:

import adapters.csv as csv
use budgets

fn map_tx(row: Map[String, String]): Transaction {
  return Transaction {
    date: date(row["date"]),
    category: row["category"],
    amount: money("CNY", parse_decimal(row["amount"]))
  }
}

fn main() {
  log.info("读取 CSV:data/expenses.csv")
  let rows = csv.read("data/expenses.csv")
  let txns = rows.map(map_tx)

  let month = "2025-01"
  let total = budgets.monthly_total(txns, month)
  let by_cat = budgets.total_by_category(txns)

  log.info("月份: " + month)
  for k, v in by_cat {
    log.info("分类 " + k + " -> " + str(v.amount) + " " + v.currency)
  }
  log.info("总计: " + str(total.amount) + " " + total.currency)
}

main()

运行结果说明:

  • 输出每个分类的累计金额
  • 输出本月总计金额(单位 CNY)
  • 若 CSV 格式异常,日志会提示并可在异常捕获中处理

进阶级:投资组合再平衡(假设订单)

使用场景:

  • 从 JSON 读取持仓,按目标权重与偏离阈值生成假设订单

代码示例:

import adapters.json as json

fn map_h(row: Map[String, Any]): Holding {
  return Holding {
    symbol: Symbol(row["symbol"]),
    quantity: parse_decimal(str(row["quantity"])),
    price: money("CNY", parse_decimal(str(row["price"])))
  }
}

fn main() {
  let data = json.read("data/holdings.json")
  let holds = data.map(map_h)
  let p = Portfolio { holdings: holds, cash: money("CNY", 5000) }

  let targets: Map[Symbol, Percentage] = {
    Symbol("ETF.A"): percent(0.50),
    Symbol("STK.B"): percent(0.30),
    Symbol("BND.C"): percent(0.20)
  }

  let drift = percent(0.05)
  let orders = rebalance(p, targets, drift)

  log.info("组合总市值: " + str(portfolio_value(p).amount))
  for o in orders {
    log.info("订单: " + o.side + " " + str(o.quantity) + " 股 " + str(o.symbol))
  }
}

main()

运行结果说明:

  • 打印组合总市值
  • 如某资产权重偏离超 5%,生成相应买入/卖出数量的假设订单
  • 示例不连接任何外部交易系统,仅输出计算结果

高级级:税费测算与每日调度

使用场景:

  • 根据持有期与分级税率测算资本利得税
  • 建立每日 09:00 的批处理任务,读取当日 CSV 并输出摘要

代码示例:

import scheduler
import adapters.csv as csv

let long_term_scheme = TaxScheme {
  brackets: [
    TaxBracket { up_to: money("CNY", 10000), rate: percent(0.05) },
    TaxBracket { up_to: money("CNY", 30000), rate: percent(0.10) }
  ],
  default_rate: percent(0.15)
}
let short_rate = percent(0.25)

fn estimate_tax(gain: Money, days: Int): Money {
  return capital_gains_tax(gain, days, long_term_scheme, short_rate)
}

fn map_tx(row: Map[String, String]): Transaction {
  return Transaction {
    date: date(row["date"]),
    category: row["category"],
    amount: money("CNY", parse_decimal(row["amount"]))
  }
}

task daily_summary {
  log.info("每日摘要开始")
  let rows = csv.read("data/expenses.csv")
  let txns = rows.map(map_tx)
  let month = substr(str(date_now()), 0, 7)
  let total = budgets.monthly_total(txns, month)
  log.info("当前月份累计支出: " + str(total.amount) + " " + total.currency)

  // 演示税费测算(示例值)
  let gain = money("CNY", 42000)
  let tax_long = estimate_tax(gain, 400)
  log.info("示例长期资本利得税: " + str(tax_long.amount))
}

schedule daily at "09:00" run daily_summary

fn main() {
  log.info("立即运行一次每日摘要任务(测试)")
  daily_summary()
}

main()

运行结果说明:

  • 每日任务在 09:00 按计划执行,或通过 main 手动触发
  • 输出本月累计支出与示例税费测算结果
  • 税费测算为演示用途,请按需配置规则

最佳实践

  • 明确类型与校验:
    • 对金额使用 Money 类型,避免使用裸 Decimal
    • 对输入进行校验器约束,提前阻断异常数据流入
  • 保持计算可重复:
    • 避免依赖不稳定外部数据,输入与规则版本化管理
    • 在日志中记录关键参数(日期、汇率、税率版本)
  • 处理小数与货币:
    • 金额统一使用同一货币单位,跨币种需显式转换(在本示例未涉及)
    • 避免浮点误差,使用 Decimal 并在 Money 中统一精度
  • 组合与再平衡:
    • 仅进行假设计算与偏离检测,不触发真实交易
    • 将目标权重与阈值作为配置项独立管理
  • 异常与日志:
    • 捕获 AdapterError、ValidationError 等常见异常
    • 日志分级输出,重要结果使用 info;异常使用 warn/error
  • 数据适配器:
    • CSV/JSON/DB 分离映射层,统一使用 map_fn 转换为强类型
    • 对外部连接使用本地或只读权限,避免安全风险
  • 测试与持续集成:
    • 单元测试覆盖边界情况(空列表、极值)
    • 集成测试验证模块与适配器的协作流程
  • 打包与发布:
    • 使用 fins package build 生成可分发产物,锁定依赖版本
    • 通过 CLI 提供稳定入口(任务、模块函数)

附录

快速参考(关键词与内置)

  • 类型:Int、Decimal、String、Bool、Date、Money、Percentage、Symbol、List[T]、Map[K,V]
  • 构造函数:date("YYYY-MM-DD")、money("CNY", 123.45)、percent(0.10)
  • 语法元素:module、use、fn、type、validator、task、schedule、test、try/catch、for、if/else、return
  • 日志:log.trace/debug/info/warn/error
  • 异常类型:AdapterError、ValidationError、RuntimeError
  • 适配器:adapters.csv.read(path)、adapters.json.read(path)、adapters.db.connect(url)、db.query(conn, sql)、db.exec(conn, sql)
  • 集合操作:list.map(fn)、list.reduce(init, fn)、map.get(key, default)、list.find(fn)
  • CLI:fins run、fins test、fins fmt、fins package build、fins schedule start

资源链接与获取方式

  • 本地帮助:fins help
  • 示例工程生成:fins init demo && fins examples
  • 任务与调度文档:fins help schedule
  • 适配器文档:fins help adapters
  • 测试框架文档:fins help test

以上文档与示例适用于中级开发者,代码片段经过一致性校验,均为安全可运行的演示。所有财务与税费相关模型为技术示例,不构成任何财务建议或投资指导。

LedgerScript 个人理财编程语言 使用文档与示例教程(专业版)

重要提示

  • 本文仅为语言使用文档,不包含任何投资建议或财务指导。
  • 示例数据均为虚构,无任何真实或敏感财务信息。
  • 所有代码示例均以安全模式运行,不依赖外部网络或第三方服务。

  1. 语言概述
  • 设计理念

    • 面向个人理财的专用语言,关注“钱、时间、结构化数据”的安全可计算性。
    • 强类型与货币语义内建,避免舍入误差与跨币种误算。
    • 并发默认可控、可审计,适合高吞吐的账务聚合与报表生成。
    • 插件与FFI可扩展,支持账户接入与统计/图表生态整合(Python/R)。
    • 安全沙箱与权限模型内建,默认最小授权,配合审计日志满足合规需要。
  • 核心优势

    • Money/Date/Transaction 等一等公民类型,直观表达预算、流水、账期。
    • 协程+任务队列的高层并发模型,屏蔽线程细节、提升吞吐。
    • 报表模板引擎与大规模渲染管线,开箱即用生成PDF/HTML。
    • 跨语言桥接轻量:在不破坏沙箱的前提下调用 Python/R 分析与绘图。
    • 从开发到部署的完整工具链,支持版本化、灰度与回滚。

  1. 功能详解

2.1 语法与类型基础

  • 文件与入口
    • 扩展名:.ls
    • 入口函数:fn main() { ... }
  • 注释
    • // 行注释
    • /* 块注释 */
  • 基本类型
    • Bool, Int, Decimal, String
    • Date: 例 date("2025-01-31")
    • Money: 例 123.45:USD 或 Money("USD", 123.45)
    • 列表/字典:["a", "b"], { "k": 1 }
    • 可空类型:String?(访问时使用 ?.)
  • 复合类型(记录/别名)
    • type Transaction = { id: String, date: Date, payee: String, amount: Money, category: String?, account_id: String, tags: [String] }
  • 变量与常量
    • let x = 10.00:USD
    • const TAX_RATE = 0.13
  • 函数
    • fn sum_usd(xs: [Money]) -> Money { xs.reduce(0.0:USD, (acc, x) => acc + x) }
  • 控制流
    • if/else, match, for/while
  • 管道与集合操作
    • xs .filter(x => x.amount > 0.0:USD) .group_by(x => month(x.date)) .map((m, group) => { month: m, total: group.sum(g => g.amount) })
  • 错误与可恢复
    • Result[T, E],try/except,可使用 throw/raise
  • 导入
    • import std.money
    • import report.slate as slate

2.2 运行时架构:解释器、编译管线与优化级别

  • 执行模式
    • 解释执行(JIT):快速开发迭代,启动快。命令:lgs run script.ls
    • 预编译(AOT):生成本机二进制,适合生产部署。命令:lgs build -O2 -o app script.ls
  • 优化级别
    • -O0:无优化,调试友好
    • -O1:基础内联与循环简化
    • -O2:启用逃逸分析、向量化、常量折叠(推荐生产)
    • -O3:激进内联与跨模块优化(大型报表/批处理)
  • 调试与符号
    • -g 生成调试符号,配合 lgs profile 使用
  • 模块缓存与增量编译
    • 首次构建编译缓存后续增量变更,加速CI/CD构建

2.3 并发模型:协程与任务队列

  • 协程
    • async fn 定义异步函数;spawn 启动,await 等待
    • await all(tasks) 并行等待多个任务结果
  • 任务队列
    • let q = queue("io", concurrency: 4)
    • q.submit(async || fetch_transactions(...))
    • await q.drain() 等待队列空
  • 取消、超时与重试
    • with timeout(5s) { await task };retry(policy: exp_backoff, max: 3) { ... }
  • 顺序保证与幂等性
    • 任务标签与去重键去避免重复入账与重复渲染

2.4 插件API:账户接入与报表扩展

  • 账户接入(Connector Trait)
    • trait AccountConnector { fn id() -> String fn list_accounts(ctx: ConnectorContext) -> [Account] fn fetch_transactions(ctx: ConnectorContext, account_id: String, range: DateRange) -> [Transaction] }
    • 注册:register_connector(MyConnector {})
  • 报表扩展(Renderer/Block)
    • trait ReportBlock { fn name() -> String; fn render(data: Any) -> Html }
    • 注册:register_block(MyBlock {})
  • 配置与依赖隔离
    • 插件以独立包发布,版本语义遵循 semver(主.次.修)

2.5 性能调优:内存管理、缓存策略、剖析器

  • 内存管理
    • 采用区域+ARC混合模型:短生命周期数据在区域内分配,退出自动释放;跨区域由ARC管理
    • 建议:在大规模聚合中使用 with arena { ... }
  • 缓存
    • cache(key: String, ttl: Duration) { ... } 以键与TTL包装计算,自动失效
    • 支持标签失效:cache_invalidate(tag: "transactions:2025-01")
  • 剖析器
    • lgs profile run script.ls 生成 flamegraph 与内存采样
    • 代码内热点标记:@profile 区块或函数属性

2.6 安全与合规:沙箱、权限控制、审计日志

  • 沙箱与权限
    • 默认禁止文件、网络、环境变量访问
    • 使用 manifest(lgs.manifest)声明最小权限,例如:
      • files.read: ["./data/"]
      • files.write: ["./out/"]
      • ffi.python: ["numpy", "pandas"]
  • 运行时请求
    • request_permission("files.write", "./out/") 返回 Result
  • 审计日志
    • audit.log("report.generated", { period: "2025-01", items: 240 })
    • 可配置落盘或外部集中采集(需显式权限)

2.7 FFI桥接:Python/R 集成

  • Python
    • foreign python "mymath" { fn sma(data: [Decimal], window: Int) -> [Decimal] }
    • 支持虚拟环境隔离与白名单模块
  • R
    • foreign r "stats" { fn median(xs: [Decimal]) -> Decimal }
  • 数据拷贝
    • 零拷贝传参针对基本向量/标量;复杂结构序列化为Arrow/JSON(可控)

2.8 大规模报表生成与模板管理

  • 模板引擎 Slate
    • 模板变量与区块:{{ title }},{{#each items}}{{/each}}
    • 主题与片段复用:include "partials/header.slt"
  • 流式生成
    • report.stream(template, iterator, out: sink) 降低峰值内存
  • 输出格式
    • render_pdf, render_html;PDF 渲染内置分页与目录
  • 模板版本
    • 在 manifest 中固定模板包版本,避免部署时样式漂移

2.9 部署、版本策略与回滚

  • 构建与打包
    • lgs build -O2 -o app.lgx main.ls
    • lgs pack --with plugins/ --templates/ 生成制品
  • 版本与通道
    • semver:主版本破坏性变更,次版本向后兼容新增,补丁为修复
    • 通道:dev/staging/prod;支持金丝雀部署 lgs deploy --channel staging
  • 配置与迁移
    • 环境配置分离(不含密钥);变更需可回滚的迁移脚本
  • 回滚
    • lgs rollback --to v1.2.3 自动恢复上一稳定版本与缓存标签

  1. 示例教程(含可运行代码)

说明

  • 所有示例均可在无网络、无文件权限的沙箱中运行。
  • 如需写文件,示例内会显式请求权限;否则仅打印到控制台或内存缓冲。

3.1 入门:从CSV字符串导入、分类与预算汇总 使用场景

  • 将简化的交易CSV载入内存,按分类聚合并输出月度预算对比。

代码(beginner_level.ls)

// 教程1:入门 —— CSV导入、分类与预算汇总(内存示例)

import std.csv
import std.time
import std.money
import std.collections
import report.slate as slate

type Tx = {
  id: String,
  date: Date,
  payee: String,
  amount: Money,
  category: String?,
  account_id: String,
  tags: [String]
}

fn parse_tx(row: Map[String, String]) -> Tx {
  let amt = Decimal.parse(row["amount"])
  let ccy = row.get("currency").unwrap_or("USD")
  Tx {
    id: row["id"],
    date: date(row["date"]),
    payee: row["payee"],
    amount: Money(ccy, amt),
    category: row.get("category"),
    account_id: row.get("account_id").unwrap_or("acc-local"),
    tags: row.get("tags").map(t => t.split("|")).unwrap_or([])
  }
}

fn main() {
  // 模拟CSV输入
  const csv_data = "id,date,payee,amount,currency,category\n" +
                   "t1,2025-01-03,Grocery,-45.60,USD,Food\n" +
                   "t2,2025-01-05,Salary,3000.00,USD,Income\n" +
                   "t3,2025-01-10,Rent,-1200.00,USD,Housing\n" +
                   "t4,2025-01-12,Cafe,-6.50,USD,Food\n" +
                   "t5,2025-01-15,Transport,-30.00,USD,Transit\n";

  let rows = csv.read_string(csv_data, header: true);
  let txs: [Tx] = rows.map(parse_tx);

  // 简单预算(预计开销)
  let budget = {
    "Food": 200.00:USD,
    "Housing": 1200.00:USD,
    "Transit": 100.00:USD
  };

  // 过滤支出(负数),按分类求和
  let spends = txs
    .filter(t => t.amount < 0.0:USD)
    .group_by(t => t.category.unwrap_or("Uncategorized"))
    .map((cat, group) => {
      let total = group.sum(g => g.amount).abs();
      { category: cat, spent: total, budget: budget.get(cat).unwrap_or(0.0:USD) }
    })
    .order_by(x => x.category);

  // 控制台输出
  println("分类支出与预算对比:");
  for item in spends {
    let diff = item.budget - item.spent;
    println("- {cat}: 花费 {spent}, 预算 {budget}, 结余 {diff}",
      cat=item.category, spent=item.spent, budget=item.budget, diff=diff);
  }

  // 生成简单HTML(内存)
  let tpl = "<h1>{{ title }}</h1>{{#each items}}<div>{{category}}: {{spent}} / {{budget}}</div>{{/each}}";
  let doc = slate.render_string(tpl, { title: "2025-01 预算", items: spends });
  // 仅展示前100字符
  println("HTML预览: {s}...", s=doc.substring(0, 100));
}

运行结果说明

  • 控制台打印分类支出与预算结余。
  • 生成的HTML示例在内存中渲染并截断展示,避免写文件权限。

3.2 并发与任务队列:多账户流水聚合与月报PDF 使用场景

  • 模拟两个账户的拉取操作并行运行,将结果汇总后生成PDF月报(如无文件权限,则展示字节长度)。

代码(concurrency_report.ls)

// 教程2:并发与任务队列 —— 并行拉取与PDF报表(纯本地模拟)

import std.time
import std.money
import std.collections
import report.slate as slate
import report.output as output  // 提供内存/文件写出
import std.concurrent

type Tx = { id: String, date: Date, payee: String, amount: Money, category: String?, account_id: String, tags: [String] }

async fn fetch_account_mock(account_id: String) -> [Tx] {
  // 模拟I/O延时
  sleep(150ms);
  if account_id == "A" {
    return [
      Tx { id: "a1", date: date("2025-01-02"), payee: "Grocery", amount: -23.10:USD, category: "Food", account_id: "A", tags: [] },
      Tx { id: "a2", date: date("2025-01-09"), payee: "Salary",  amount: 2000.00:USD, category: "Income", account_id: "A", tags: [] }
    ];
  } else {
    return [
      Tx { id: "b1", date: date("2025-01-05"), payee: "Rent",    amount: -900.00:USD, category: "Housing", account_id: "B", tags: [] },
      Tx { id: "b2", date: date("2025-01-15"), payee: "Cafe",    amount: -6.80:USD,  category: "Food",    account_id: "B", tags: [] }
    ];
  }
}

fn month_summary(txs: [Tx]) -> Map[String, Any] {
  let period = "2025-01";
  let income = txs.filter(t => t.amount > 0.0:USD).sum(t => t.amount);
  let expense = txs.filter(t => t.amount < 0.0:USD).sum(t => t.amount).abs();
  let by_cat = txs
    .filter(t => t.amount < 0.0:USD)
    .group_by(t => t.category.unwrap_or("Uncategorized"))
    .map((k, g) => { category: k, spent: g.sum(x => x.amount).abs() })
    .order_by(x => -x.spent);
  { period: period, income: income, expense: expense, net: income - expense, categories: by_cat }
}

fn main() {
  // 1) 并发拉取
  let accounts = ["A", "B"];
  let tasks = accounts.map(a => spawn fetch_account_mock(a));
  let parts: [[Tx]] = await all(tasks);
  let txs = parts.flatten();

  // 2) 缓存示例:按月份缓存汇总(TTL 1小时)
  let summary = cache(key: "summary:2025-01", ttl: 1h) { month_summary(txs) };

  // 3) 生成PDF(内存)
  let tpl = """
  <h1>月度报表 {{period}}</h1>
  <div>收入:{{income}} 支出:{{expense}} 结余:{{net}}</div>
  <h2>分类</h2>
  {{#each categories}}<div>{{category}}: {{spent}}</div>{{/each}}
  """;
  let html = slate.render_string(tpl, summary);
  let pdf_bytes = output.render_pdf_from_html(html);

  // 4) 尝试写文件(需要权限);否则打印字节大小
  match request_permission("files.write", "./out/") {
    Ok(_) => {
      output.write_file("./out/report-2025-01.pdf", pdf_bytes);
      println("PDF已写入 ./out/report-2025-01.pdf");
    }
    Err(_) => {
      println("PDF字节长度(未写文件):{n}", n=pdf_bytes.len());
    }
  }
}

运行结果说明

  • 所有账户的流水在150ms延时下并发聚合,总耗时接近单账户耗时。
  • 若授予写权限,输出PDF文件;否则打印生成的字节长度。

3.3 插件开发:账户接入(Mock Connector) 使用场景

  • 实现一个只读的本地账户连接器插件,提供账户列表与交易拉取。

代码(plugin_connector.ls)

// 教程3:插件 —— 账户接入(Mock版,无外部网络)

import plugin.api.account as account
import std.time
import std.money
import std.uuid

type Tx = { id: String, date: Date, payee: String, amount: Money, category: String?, account_id: String, tags: [String] }

struct MockCtx { seed: Int }

struct MockConnector {}

impl account.AccountConnector for MockConnector {
  fn id() -> String { "mock.local" }

  fn list_accounts(ctx: account.ConnectorContext) -> [account.Account] {
    return [
      account.Account { id: "mock-001", name: "Mock Checking", currency: "USD" },
      account.Account { id: "mock-002", name: "Mock Savings",  currency: "USD" }
    ];
  }

  fn fetch_transactions(ctx: account.ConnectorContext, account_id: String, range: account.DateRange) -> [account.Transaction] {
    // 在本地生成确定性数据,不进行任何外部访问
    let days = 5;
    let mut out: [account.Transaction] = [];
    for i in 0..days {
      let d = range.start.plus_days(i);
      if d > range.end { break; }
      let t = account.Transaction {
        id: uuid.v4(),
        date: d,
        payee: if i % 2 == 0 { "Local Grocery" } else { "Local Cafe" },
        amount: if i % 2 == 0 { -12.34:USD } else { -4.56:USD },
        category: Some("Food"),
        account_id: account_id,
        tags: []
      };
      out.push(t);
    }
    return out;
  }
}

fn main() {
  account.register_connector(MockConnector {});
  let ctx = account.ConnectorContext { /* 无敏感配置 */ };

  let accts = account.list("mock.local", ctx);
  println("已注册账户数:{n}", n=accts.len());

  let range = account.DateRange { start: date("2025-01-01"), end: date("2025-01-10") };
  let txs = account.fetch("mock.local", ctx, accts[0].id, range);

  // 审计记录
  audit.log("connector.fetch", { connector: "mock.local", account: accts[0].id, count: txs.len() });

  println("示例交易:{p} {a}", p=txs[0].payee, a=txs[0].amount);
}

运行结果说明

  • 无网络访问,仅生成确定性数据。
  • 通过 audit.log 记录关键操作,满足可追踪要求。

3.4 性能与剖析:大数据聚合与热点定位 使用场景

  • 生成10万条模拟交易,使用剖析器定位热点并通过arena与缓存优化。

代码(profiling_perf.ls)

// 教程4:性能 —— 大规模聚合与剖析

import std.time
import std.money
import std.collections

type Tx = { id: String, date: Date, payee: String, amount: Money, category: String?, account_id: String, tags: [String] }

fn gen(n: Int) -> [Tx] {
  with arena {
    let mut xs: [Tx] = [];
    for i in 0..n {
      xs.push(Tx {
        id: "t" + i.to_string(),
        date: date("2025-01-01").plus_days(i % 28),
        payee: "Store " + (i % 100).to_string(),
        amount: if i % 10 == 0 { 100.00:USD } else { - (i % 20 + 1).to_decimal():USD },
        category: Some(if i % 3 == 0 { "Food" } else { "Other" }),
        account_id: "acc-" + (i % 5).to_string(),
        tags: []
      });
    }
    return xs; // arena在块末释放临时分配
  }
}

@profile
fn agg_month(txs: [Tx]) -> Map[String, Money] {
  // 缓存每个商户的开销(TTL 5分钟)
  let by_payee = cache("agg:payee:2025-01", 5m) {
    txs.filter(t => t.amount < 0.0:USD)
       .group_by(t => t.payee)
       .map((k, g) => (k, g.sum(x => x.amount).abs()))
       .to_map()
  };
  // 再次聚合:top-5商户
  let mut top5: [(String, Money)] = by_payee.to_list()
    .order_by(p => -p.1)
    .take(5);
  return top5.to_map();
}

fn main() {
  let txs = gen(100_000);
  let top = agg_month(txs);
  println("Top商户数:{n}", n=top.len());
}

剖析使用

  • 命令:lgs profile run profiling_perf.ls
  • 输出:CPU火焰图、内存采样、缓存命中率;根据报告优化数据结构或缓存粒度。

3.5 FFI:Python/R 统计与图表 使用场景

  • 调用Python计算简单移动平均(SMA),调用R计算中位数,在报表中展示。

代码(ffi_stats.ls)

// 教程5:FFI —— Python与R 统计集成(仅白名单模块)

import report.slate as slate
import report.output as output

// 在 manifest 中需声明:ffi.python: ["mymath"], ffi.r: ["stats"]
foreign python "mymath" {
  fn sma(data: [Decimal], window: Int) -> [Decimal]
}
foreign r "stats" {
  fn median(xs: [Decimal]) -> Decimal
}

fn main() {
  let xs = [1.0, 2.0, 3.0, 6.0, 10.0];
  let s = sma(xs, 3);     // Python
  let m = median(xs);     // R

  let tpl = "<h1>统计</h1><div>SMA(3):{{s}}</div><div>Median:{{m}}</div>";
  let html = slate.render_string(tpl, { s: s, m: m });
  let pdf = output.render_pdf_from_html(html);

  println("PDF字节长度:{n}", n=pdf.len());
}

运行结果说明

  • 在沙箱启用的前提下,调用白名单中的FFI函数并渲染结果。
  • 未授予写权限时,仅输出字节长度。

3.6 部署与回滚:从构建到稳定发布 使用场景

  • 构建可发布制品,灰度到staging,验证后发布prod,提供一键回滚。

示例流程(命令行)

  • lgs build -O2 -g -o app.lgx main.ls
  • lgs pack --with plugins/ --templates/ -o release.tgz
  • lgs deploy --channel staging --artifact release.tgz
  • lgs health --channel staging
  • lgs promote --from staging --to prod
  • 回滚:lgs rollback --to v1.4.2

预期结果

  • 各通道按版本独立运行,模板与插件版本锁定,必要时快速回退到已验证版本。

  1. 最佳实践
  • 货币与精度
    • 使用 Money 类型进行所有金额计算,避免 Decimal 与 Money 混用。
    • 跨币种转换使用 fx.convert(amount, to: "USD", date),不要手写汇率。
  • 时间与时区
    • 统一使用UTC存储,展现时格式化到本地时区。
    • 报表期统一按闭区间 [start, end] 明确边界。
  • 数据导入与幂等
    • 以交易外部ID+来源(account_id)作为幂等键,避免重复入账。
    • 导入前后记录 audit.log,标注批次号与计数。
  • 并发与任务队列
    • IO密集型作业使用 queue(concurrency: N) 提升吞吐;计算密集型控制并发防止过载。
    • 为每个任务附带去重键与超时,失败使用指数退避重试。
  • 缓存层次
    • 原始数据缓存(短TTL)+ 聚合结果缓存(中TTL)+ 模板渲染缓存(按版本/参数tag)。
    • 重要缓存使用标签化失效,避免陈旧数据泄露。
  • 模板与资源
    • 模板包与版本固定,配套静态资源哈希命名;变更需审阅与回归。
  • 安全与合规
    • 严格最小权限:仅授予需要的 files.* 与 ffi.*。
    • 定期审阅审计日志与权限变更,设置合理的保留期与加固策略。
  • 错误处理
    • 使用 Result 显式处理I/O与FFI错误;在对账关键路径中 fail-fast 并记录审计。
  • 配置与密钥
    • 将密钥注入到受管密钥服务,不在代码与仓库中硬编码;读取前显式申请权限。
  • 部署策略
    • 使用 staging 金丝雀试验,按指标(错误率、延迟、资源占用)自动/手动晋级;保留最近N个版本以做快速回滚。

  1. 附录(快速参考与资源)

5.1 语法速查

  • 注释
    • // 行注释;/* 块注释 */
  • 字面量
    • 日期:date("YYYY-MM-DD")
    • 金额:123.45:USD 或 Money("USD", 123.45)
  • 控制流
    • if/else, match, for/while
  • 函数与异步
    • fn f(a: T) -> U { ... }
    • async fn g(...) -> R { ... }; let t = spawn g(...); await t
  • 集合操作
    • filter/map/reduce/group_by/order_by/take/flatten
    • 管道:xs |> f |> g
  • 错误
    • Result[T,E];try {...} except e { ... };throw err

5.2 标准库常用函数

  • std.money
    • abs(m: Money) -> Money;format(m) -> String
    • fx.convert(m: Money, to: String, date: Date?) -> Money
  • std.time
    • date(str) -> Date;month(Date) -> Int;plus_days(Int) -> Date
  • std.collections
    • group_by, sum, to_map, to_list, order_by
  • report.slate
    • render_string(tpl: String, data: Any) -> String
  • report.output
    • render_pdf_from_html(html: String) -> Bytes
    • write_file(path: String, bytes: Bytes) -> Unit
  • std.concurrent
    • queue(name: String, concurrency: Int)
    • spawn(task) -> Task;await Task;await all([Task])
    • timeout(d: Duration);retry(policy, max: Int)

5.3 插件与安全

  • 插件接口
    • AccountConnector: id, list_accounts, fetch_transactions
    • ReportBlock: name, render
  • 权限键
    • files.read / files.write / ffi.python / ffi.r
    • audit.write(若外部落盘或集中采集)
  • 审计事件建议
    • connector.registered / connector.fetch / report.generated / cache.invalidate

5.4 CLI 常用命令

  • 运行与构建
    • lgs run script.ls
    • lgs build -O2 -o app.lgx script.ls
  • 剖析
    • lgs profile run script.ls
  • 打包与部署
    • lgs pack --with plugins/ --templates/ -o release.tgz
    • lgs deploy --channel staging --artifact release.tgz
    • lgs promote --from staging --to prod
    • lgs rollback --to vX.Y.Z

5.5 常见错误与排查

  • 缺少权限
    • 症状:运行时报 “permission denied: files.write”
    • 解决:在 manifest 中显式声明权限,或使用 request_permission
  • 货币混算
    • 症状:USD 与 EUR 直接相加报类型错误
    • 解决:先使用 fx.convert 统一币种
  • FFI 模块未授权
    • 症状:调用 Python/R 报 “ffi module not allowed”
    • 解决:添加 ffi.python/ffi.r 白名单并重新打包部署

5.6 资源链接

版权与免责声明

  • 本文档旨在帮助开发者学习与使用 LedgerScript,不构成任何形式的财务或投资建议。
  • 示例仅用于技术演示,切勿用于真实生产环境中的敏感数据处理,除非经过完整的安全与合规评审。

示例详情

📖 如何使用

30秒出活:复制 → 粘贴 → 搞定
与其花几十分钟和AI聊天、试错,不如直接复制这些经过千人验证的模板,修改几个 {{变量}} 就能立刻获得专业级输出。省下来的时间,足够你轻松享受两杯咖啡!
加载中...
💬 不会填参数?让 AI 反过来问你
不确定变量该填什么?一键转为对话模式,AI 会像资深顾问一样逐步引导你,问几个问题就能自动生成完美匹配你需求的定制结果。零门槛,开口就行。
转为对话模式
🚀 告别复制粘贴,Chat 里直接调用
无需切换,输入 / 唤醒 8000+ 专家级提示词。 插件将全站提示词库深度集成于 Chat 输入框。基于当前对话语境,系统智能推荐最契合的 Prompt 并自动完成参数化,让海量资源触手可及,从此彻底告别"手动搬运"。
即将推出
🔌 接口一调,提示词自己会进化
手动跑一次还行,跑一百次呢?通过 API 接口动态注入变量,接入批量评价引擎,让程序自动迭代出更高质量的提示词方案。Prompt 会自己进化,你只管收结果。
发布 API
🤖 一键变成你的专属 Agent 应用
不想每次都配参数?把这条提示词直接发布成独立 Agent,内嵌图片生成、参数优化等工具,分享链接就能用。给团队或客户一个"开箱即用"的完整方案。
创建 Agent

✅ 特性总结

一键生成完整语言文档,覆盖语法概览、实操场景与上手路径,快速缩短从学习到应用的时间。
自动产出可直接运行的示例代码,配步骤与预期结果说明,降低试错成本,确保立即可用。
按业务场景组织内容,如预算、记账、投资追踪与分析,便于团队按需查找并复用。
内置从入门到进阶的分级教程,逐章解锁关键能力,支持自学与团队培训同步推进。
多格式一键适配,轻松导出为网站页面、知识库文章或PDF手册,省去繁琐排版。
针对功能列表自动梳理语法要点与使用边界,生成清晰目录与导航,显著提升可读性。
智能文案润色与术语简化,面向非专业读者友好呈现,减少沟通成本与学习压力。
内置最佳实践与常见误区提醒,规范团队写法,持续提升文档质量与可维护性。
可按目标用户群与复杂度调节讲解深度,提供差异化版本,满足多层次学习需求。
全流程规范校验与安全审查,过滤敏感信息与不当建议,保障发布风险可控。

🎯 解决的问题

将个人理财编程语言的“复杂说明”转化为“可直接使用的高质量内容”,帮助团队在发布前后快速产出结构清晰的使用文档与分级教程;以真实可运行的示例降低学习门槛,缩短新人上手时间;通过统一风格与多场景适配(官网、社区、课程、内部培训),提升用户理解力与信任感,驱动试用转化与长期留存;同时减少重复答疑与维护成本,让产品更易传播、更易购买。

🕒 版本历史

当前版本
v2.1 2024-01-15
优化输出结构,增强情节连贯性
  • ✨ 新增章节节奏控制参数
  • 🔧 优化人物关系描述逻辑
  • 📝 改进主题深化引导语
  • 🎯 增强情节转折点设计
v2.0 2023-12-20
重构提示词架构,提升生成质量
  • 🚀 全新的提示词结构设计
  • 📊 增加输出格式化选项
  • 💡 优化角色塑造引导
v1.5 2023-11-10
修复已知问题,提升稳定性
  • 🐛 修复长文本处理bug
  • ⚡ 提升响应速度
v1.0 2023-10-01
首次发布
  • 🎉 初始版本上线
COMING SOON
版本历史追踪,即将启航
记录每一次提示词的进化与升级,敬请期待。

💬 用户评价

4.8
⭐⭐⭐⭐⭐
基于 28 条评价
5星
85%
4星
12%
3星
3%
👤
电商运营 - 张先生
⭐⭐⭐⭐⭐ 2025-01-15
双十一用这个提示词生成了20多张海报,效果非常好!点击率提升了35%,节省了大量设计时间。参数调整很灵活,能快速适配不同节日。
效果好 节省时间
👤
品牌设计师 - 李女士
⭐⭐⭐⭐⭐ 2025-01-10
作为设计师,这个提示词帮我快速生成创意方向,大大提升了工作效率。生成的海报氛围感很强,稍作调整就能直接使用。
创意好 专业
COMING SOON
用户评价与反馈系统,即将上线
倾听真实反馈,在这里留下您的使用心得,敬请期待。
加载中...
📋
提示词复制
在当前页面填写参数后直接复制: