Swift实现股票图:从基础到高级

目录

    • 一、核心实现方案
      • 1. 原生方案:使用 Core Graphics 绘制
      • 2. 使用第三方库:Charts
      • 3. 跨平台方案:使用 SwiftUI + Canvas
    • 二、技术指标实现
      • 1. 移动平均线 (MA)
      • 2. 布林带 (Bollinger Bands)
      • 3. MACD (Moving Average Convergence Divergence)
    • 三、性能优化策略
      • 1. 数据分页与懒加载
      • 2. 离屏渲染优化
      • 3. 手势交互优化
    • 四、高级功能实现
      • 1. 十字线光标
      • 2. 多时间周期切换
    • 五、架构设计建议
      • 1、分层架构设计:
      • 2、性能优化总结表:
      • 3、跨平台适配方案:
    • 六、推荐方案选择
      • 1、根据需求选择方案
      • 2、性能对比表

股票图(尤其是K线图)是金融应用的核心功能。下面我将详细介绍多种实现方案,并提供完整的代码示例和优化建议。

一、核心实现方案

1. 原生方案:使用 Core Graphics 绘制

优势:

  • 完全可控,无依赖
  • 高性能,适合大数据量
  • 定制化程度高

实现代码:

import UIKitstruct Candle {let date: Datelet open: Doublelet high: Doublelet low: Doublelet close: Doublelet volume: Double
}class StockChartView: UIView {var candles: [Candle] = [] {didSet {calculateMetrics()setNeedsDisplay()}}// 计算指标private var minPrice: Double = 0private var maxPrice: Double = 0private var maxVolume: Double = 0private var candleWidth: CGFloat = 0private var candleSpacing: CGFloat = 2override func draw(_ rect: CGRect) {super.draw(rect)guard let context = UIGraphicsGetCurrentContext() else { return }guard !candles.isEmpty else { return }drawGrid(context: context)drawCandles(context: context)drawVolume(context: context)drawIndicators(context: context)}private func calculateMetrics() {minPrice = candles.map { $0.low }.min() ?? 0maxPrice = candles.map { $0.high }.max() ?? 0// 添加10%的上下空间let priceRange = maxPrice - minPriceminPrice = minPrice - priceRange * 0.1maxPrice = maxPrice + priceRange * 0.1maxVolume = candles.map { $0.volume }.max() ?? 0// 计算蜡烛宽度let availableWidth = bounds.width - 40 // 左右边距candleWidth = (availableWidth / CGFloat(candles.count)) - candleSpacing}private func drawGrid(context: CGContext) {context.setStrokeColor(UIColor.systemGray4.cgColor)context.setLineWidth(0.5)// 水平网格线let horizontalLines = 5for i in 0...horizontalLines {let y = bounds.height * CGFloat(i) / CGFloat(horizontalLines)context.move(to: CGPoint(x: 0, y: y))context.addLine(to: CGPoint(x: bounds.width, y: y))}// 垂直网格线let verticalLines = 6for i in 0...verticalLines {let x = bounds.width * CGFloat(i) / CGFloat(verticalLines)context.move(to: CGPoint(x: x, y: 0))context.addLine(to: CGPoint(x: x, y: bounds.height))}context.strokePath()}private func drawCandles(context: CGContext) {let chartHeight = bounds.height * 0.7 // 70%高度用于K线let topMargin: CGFloat = 20for (index, candle) in candles.enumerated() {let x = 20 + CGFloat(index) * (candleWidth + candleSpacing)// 计算价格对应的Y坐标func priceToY(_ price: Double) -> CGFloat {let priceRatio = (price - minPrice) / (maxPrice - minPrice)return topMargin + chartHeight * (1 - CGFloat(priceRatio))}let highY = priceToY(candle.high)let lowY = priceToY(candle.low)let openY = priceToY(candle.open)let closeY = priceToY(candle.close)// 绘制影线context.setStrokeColor(UIColor.gray.cgColor)context.setLineWidth(1)context.move(to: CGPoint(x: x + candleWidth/2, y: highY))context.addLine(to: CGPoint(x: x + candleWidth/2, y: lowY))context.strokePath()// 绘制实体let bodyHeight = max(1, abs(openY - closeY))let bodyY = min(openY, closeY)if candle.close > candle.open {context.setFillColor(UIColor.systemRed.cgColor) // 阳线} else {context.setFillColor(UIColor.systemGreen.cgColor) // 阴线}context.fill(CGRect(x: x, y: bodyY, width: candleWidth, height: bodyHeight))}}private func drawVolume(context: CGContext) {let volumeHeight = bounds.height * 0.3 // 30%高度用于成交量let volumeTop = bounds.height * 0.7 + 10for (index, candle) in candles.enumerated() {let x = 20 + CGFloat(index) * (candleWidth + candleSpacing)// 计算成交量高度let volumeRatio = CGFloat(candle.volume / maxVolume)let barHeight = volumeHeight * volumeRatio// 设置颜色(与K线一致)if candle.close > candle.open {context.setFillColor(UIColor.systemRed.cgColor)} else {context.setFillColor(UIColor.systemGreen.cgColor)}context.fill(CGRect(x: x, y: volumeTop + volumeHeight - barHeight, width: candleWidth, height: barHeight))}}private func drawIndicators(context: CGContext) {// 绘制移动平均线示例let movingAverage = calculateMovingAverage(period: 5)context.setStrokeColor(UIColor.blue.cgColor)context.setLineWidth(1.5)for (index, value) in movingAverage.enumerated() {let x = 20 + CGFloat(index) * (candleWidth + candleSpacing) + candleWidth/2let y = bounds.height * 0.7 * (1 - CGFloat((value - minPrice) / (maxPrice - minPrice))) + 20if index == 0 {context.move(to: CGPoint(x: x, y: y))} else {context.addLine(to: CGPoint(x: x, y: y))}}context.strokePath()}private func calculateMovingAverage(period: Int) -> [Double] {guard period > 0, candles.count >= period else { return [] }var averages: [Double] = []for i in 0..<candles.count {let start = max(0, i - period + 1)let end = ilet range = candles[start...end]let sum = range.reduce(0) { $0 + $1.close }averages.append(sum / Double(range.count))}return averages}
}

2. 使用第三方库:Charts

优势

  • 快速实现复杂图表
  • 内置多种技术指标
  • 支持交互功能

实现步骤:

  1. 安装 Charts 库(通过 CocoaPods 或 SPM)
  2. 创建 K 线图视图
import UIKit
import Chartsclass StockChartViewController: UIViewController {@IBOutlet weak var candleStickChartView: CandleStickChartView!@IBOutlet weak var volumeChartView: BarChartView!override func viewDidLoad() {super.viewDidLoad()setupCharts()loadData()}private func setupCharts() {// 配置K线图candleStickChartView.dragEnabled = truecandleStickChartView.setScaleEnabled(true)candleStickChartView.pinchZoomEnabled = truecandleStickChartView.xAxis.labelPosition = .bottomcandleStickChartView.legend.enabled = false// 配置成交量图volumeChartView.dragEnabled = falsevolumeChartView.setScaleEnabled(false)volumeChartView.pinchZoomEnabled = falsevolumeChartView.xAxis.labelPosition = .bottomvolumeChartView.legend.enabled = falsevolumeChartView.leftAxis.enabled = false}private func loadData() {let candles = generateSampleData()// K线数据var candleEntries = [CandleChartDataEntry]()for (i, candle) in candles.enumerated() {let entry = CandleChartDataEntry(x: Double(i),shadowH: candle.high,shadowL: candle.low,open: candle.open,close: candle.close)candleEntries.append(entry)}let candleDataSet = CandleChartDataSet(entries: candleEntries, label: "K线")candleDataSet.increasingColor = .systemRedcandleDataSet.increasingFilled = truecandleDataSet.decreasingColor = .systemGreencandleDataSet.decreasingFilled = truecandleDataSet.shadowColor = .darkGraycandleDataSet.shadowWidth = 1candleStickChartView.data = CandleChartData(dataSet: candleDataSet)// 成交量数据var volumeEntries = [BarChartDataEntry]()for (i, candle) in candles.enumerated() {volumeEntries.append(BarChartDataEntry(x: Double(i), y: candle.volume))}let volumeDataSet = BarChartDataSet(entries: volumeEntries, label: "成交量")volumeDataSet.colors = candles.map { $0.close > $0.open ? .systemRed : .systemGreen }volumeChartView.data = BarChartData(dataSet: volumeDataSet)// 添加移动平均线let movingAverage = calculateMovingAverage(data: candles.map { $0.close }, period: 5)var lineEntries = [ChartDataEntry]()for (i, value) in movingAverage.enumerated() {lineEntries.append(ChartDataEntry(x: Double(i), y: value))}let lineDataSet = LineChartDataSet(entries: lineEntries, label: "5日均线")lineDataSet.colors = [.blue]lineDataSet.drawCirclesEnabled = falselineDataSet.lineWidth = 2candleStickChartView.data?.addDataSet(lineDataSet)}private func generateSampleData() -> [Candle] {// 生成示例数据...}private func calculateMovingAverage(data: [Double], period: Int) -> [Double] {// 计算移动平均...}
}

3. 跨平台方案:使用 SwiftUI + Canvas

import SwiftUIstruct CandleChart: View {let candles: [Candle]@State private var visibleRange: ClosedRange<Int> = 0...50var body: some View {GeometryReader { geometry inZStack(alignment: .topLeading) {// 网格背景GridBackground()// K线图CandlesView(candles: Array(candles[visibleRange]),width: geometry.size.width,height: geometry.size.height * 0.7).offset(y: 20)// 成交量图VolumeView(candles: Array(candles[visibleRange]),width: geometry.size.width,height: geometry.size.height * 0.3).offset(y: geometry.size.height * 0.7 + 10)// 技术指标MovingAverageView(candles: Array(candles[visibleRange]),width: geometry.size.width,height: geometry.size.height * 0.7).offset(y: 20)}.gesture(DragGesture().onChanged { value in// 处理拖动逻辑})}}
}struct CandlesView: View {let candles: [Candle]let width: CGFloatlet height: CGFloatprivate var minPrice: Double { candles.map { $0.low }.min() ?? 0 }private var maxPrice: Double { candles.map { $0.high }.max() ?? 0 }private var candleWidth: CGFloat { (width - 40) / CGFloat(candles.count) - 2 }var body: some View {Canvas { context, size infor (index, candle) in candles.enumerated() {let x = 20 + CGFloat(index) * (candleWidth + 2) + candleWidth/2// 影线context.stroke(Path { path inpath.move(to: CGPoint(x: x, y: priceToY(candle.high)))path.addLine(to: CGPoint(x: x, y: priceToY(candle.low)))},with: .color(.gray),lineWidth: 1)// 实体let bodyRect = CGRect(x: 20 + CGFloat(index) * (candleWidth + 2),y: priceToY(max(candle.open, candle.close)),width: candleWidth,height: abs(priceToY(candle.open) - priceToY(candle.close)))context.fill(Path(bodyRect),with: .color(candle.close > candle.open ? .red : .green))}}.frame(height: height)}private func priceToY(_ price: Double) -> CGFloat {let priceRatio = (price - minPrice) / (maxPrice - minPrice)return height * (1 - CGFloat(priceRatio))}
}

二、技术指标实现

1. 移动平均线 (MA)

extension Array where Element == Candle {func movingAverage(period: Int) -> [Double] {guard period > 0, count >= period else { return [] }return self.indices.map { index inlet start = max(0, index - period + 1)let end = indexlet range = self[start...end]return range.reduce(0) { $0 + $1.close } / Double(range.count)}}
}

2. 布林带 (Bollinger Bands)

struct BollingerBands {let upper: [Double]let middle: [Double] // 即MAlet lower: [Double]
}extension Array where Element == Candle {func bollingerBands(period: Int, multiplier: Double = 2) -> BollingerBands {let ma = movingAverage(period: period)guard ma.count == count else { return BollingerBands(upper: [], middle: [], lower: []) }let stdDev: [Double] = indices.map { index inlet start = max(0, index - period + 1)let end = indexlet range = self[start...end].map { $0.close }let average = ma[index]let variance = range.reduce(0) { $0 + pow($1 - average, 2) } / Double(range.count)return sqrt(variance)}let upper = zip(ma, stdDev).map { $0 + $1 * multiplier }let lower = zip(ma, stdDev).map { $0 - $1 * multiplier }return BollingerBands(upper: upper, middle: ma, lower: lower)}
}

3. MACD (Moving Average Convergence Divergence)

struct MACD {let macdLine: [Double]let signalLine: [Double]let histogram: [Double]
}extension Array where Element == Candle {func macd(fastPeriod: Int = 12, slowPeriod: Int = 26, signalPeriod: Int = 9) -> MACD {let fastEMA = exponentialMovingAverage(period: fastPeriod)let slowEMA = exponentialMovingAverage(period: slowPeriod)let macdLine = zip(fastEMA, slowEMA).map { $0 - $1 }let signalLine = macdLine.exponentialMovingAverage(period: signalPeriod)let histogram = zip(macdLine, signalLine).map { $0 - $1 }return MACD(macdLine: macdLine, signalLine: signalLine, histogram: histogram)}private func exponentialMovingAverage(period: Int) -> [Double] {guard count >= period else { return [] }var ema: [Double] = []let multiplier = 2.0 / Double(period + 1)// 第一个EMA是简单移动平均let firstSMA = prefix(period).reduce(0) { $0 + $1.close } / Double(period)ema.append(firstSMA)// 计算后续EMAfor i in period..<count {let value = self[i].closelet prevEMA = ema[i - period]let currentEMA = (value - prevEMA) * multiplier + prevEMAema.append(currentEMA)}// 补齐前面的空值return Array(repeating: Double.nan, count: period - 1) + ema}
}

三、性能优化策略

1. 数据分页与懒加载

class StockDataLoader {private let allCandles: [Candle]private var loadedCandles: [Candle] = []private let pageSize = 100init(candles: [Candle]) {self.allCandles = candles}func loadMore(completion: @escaping ([Candle]) -> Void) {let startIndex = loadedCandles.countguard startIndex < allCandles.count else { return }let endIndex = min(startIndex + pageSize, allCandles.count)let newCandles = Array(allCandles[startIndex..<endIndex])// 模拟网络请求延迟DispatchQueue.global().asyncAfter(deadline: .now() + 0.3) {DispatchQueue.main.async {self.loadedCandles.append(contentsOf: newCandles)completion(self.loadedCandles)}}}func visibleCandles(for range: ClosedRange<Int>) -> [Candle] {guard !loadedCandles.isEmpty else { return [] }let start = max(range.lowerBound, 0)let end = min(range.upperBound, loadedCandles.count - 1)return Array(loadedCandles[start...end])}
}

2. 离屏渲染优化

class StockChartView: UIView {private var renderLayer: CALayer?override func draw(_ rect: CGRect) {if renderLayer == nil {renderOffscreen()}}private func renderOffscreen() {DispatchQueue.global(qos: .userInitiated).async {let format = UIGraphicsImageRendererFormat()format.scale = UIScreen.main.scalelet renderer = UIGraphicsImageRenderer(size: self.bounds.size, format: format)let image = renderer.image { context inself.drawContent(context: context.cgContext)}DispatchQueue.main.async {let layer = CALayer()layer.contents = image.cgImagelayer.frame = self.boundsself.layer.addSublayer(layer)self.renderLayer = layer}}}private func drawContent(context: CGContext) {// 绘制逻辑...}
}

3. 手势交互优化

class InteractiveStockChartView: StockChartView {private var panStart: CGPoint = .zeroprivate var visibleCandleCount: Int = 50override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {super.touchesBegan(touches, with: event)panStart = touches.first?.location(in: self) ?? .zero}override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {guard let touchPoint = touches.first?.location(in: self) else { return }let deltaX = panStart.x - touchPoint.xlet candleDelta = Int(deltaX / (candleWidth + candleSpacing))if abs(candleDelta) > 0 {// 更新可见范围visibleCandleCount = max(10, min(200, visibleCandleCount + candleDelta))setNeedsDisplay()panStart = touchPoint}}@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {let scale = gesture.scalelet newCount = Int(Double(visibleCandleCount) / scaleif gesture.state == .changed {visibleCandleCount = max(10, min(500, newCount))setNeedsDisplay()}}
}

四、高级功能实现

1. 十字线光标

class CrosshairView: UIView {var currentPosition: CGPoint? {didSet { setNeedsDisplay() }}override func draw(_ rect: CGRect) {guard let position = currentPosition else { return }guard let context = UIGraphicsGetCurrentContext() else { return }// 水平线context.setStrokeColor(UIColor.white.withAlphaComponent(0.7).cgColor)context.setLineWidth(0.5)context.setLineDash(phase: 0, lengths: [5, 5])context.move(to: CGPoint(x: 0, y: position.y))context.addLine(to: CGPoint(x: bounds.width, y: position.y))// 垂直线context.move(to: CGPoint(x: position.x, y: 0))context.addLine(to: CGPoint(x: position.x, y: bounds.height))context.strokePath()// 信息框drawInfoBox(at: position, in: context)}private func drawInfoBox(at position: CGPoint, in context: CGContext) {let boxWidth: CGFloat = 120let boxHeight: CGFloat = 80let boxX = position.x > bounds.width / 2 ? position.x - boxWidth - 10 : position.x + 10let boxY = position.y > bounds.height / 2 ? position.y - boxHeight : position.ylet boxRect = CGRect(x: boxX, y: boxY, width: boxWidth, height: boxHeight)// 背景context.setFillColor(UIColor.systemBackground.withAlphaComponent(0.9).cgColor)context.fill(boxRect)// 边框context.setStrokeColor(UIColor.systemGray.cgColor)context.setLineWidth(1)context.stroke(boxRect)// 绘制文本信息let paragraphStyle = NSMutableParagraphStyle()paragraphStyle.alignment = .leftlet attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 12),.paragraphStyle: paragraphStyle,.foregroundColor: UIColor.label]let dateText = "日期: 2023-06-15"let priceText = "价格: 150.25"let volumeText = "成交量: 1.2M"dateText.draw(at: CGPoint(x: boxX + 8, y: boxY + 8), withAttributes: attributes)priceText.draw(at: CGPoint(x: boxX + 8, y: boxY + 28), withAttributes: attributes)volumeText.draw(at: CGPoint(x: boxX + 8, y: boxY + 48), withAttributes: attributes)}
}

2. 多时间周期切换

enum ChartTimeframe: String, CaseIterable {case oneMinute = "1分"case fiveMinutes = "5分"case fifteenMinutes = "15分"case thirtyMinutes = "30分"case oneHour = "1小时"case fourHours = "4小时"case oneDay = "日线"case oneWeek = "周线"case oneMonth = "月线"var interval: TimeInterval {switch self {case .oneMinute: return 60case .fiveMinutes: return 300case .fifteenMinutes: return 900case .thirtyMinutes: return 1800case .oneHour: return 3600case .fourHours: return 14400case .oneDay: return 86400case .oneWeek: return 604800case .oneMonth: return 2592000 // 近似值}}
}class TimeframeSelector: UIView {var selectedTimeframe: ChartTimeframe = .oneDay {didSet { updateSelection() }}private var buttons: [UIButton] = []override init(frame: CGRect) {super.init(frame: frame)setupView()}private func setupView() {let stackView = UIStackView()stackView.axis = .horizontalstackView.distribution = .fillEquallystackView.spacing = 8for timeframe in ChartTimeframe.allCases {let button = UIButton(type: .system)button.setTitle(timeframe.rawValue, for: .normal)button.titleLabel?.font = UIFont.systemFont(ofSize: 14)button.addTarget(self, action: #selector(timeframeTapped(_:)), for: .touchUpInside)button.tag = ChartTimeframe.allCases.firstIndex(of: timeframe) ?? 0buttons.append(button)stackView.addArrangedSubview(button)}addSubview(stackView)stackView.translatesAutoresizingMaskIntoConstraints = falseNSLayoutConstraint.activate([stackView.topAnchor.constraint(equalTo: topAnchor),stackView.bottomAnchor.constraint(equalTo: bottomAnchor),stackView.leadingAnchor.constraint(equalTo: leadingAnchor),stackView.trailingAnchor.constraint(equalTo: trailingAnchor)])updateSelection()}private func updateSelection() {for (index, button) in buttons.enumerated() {let isSelected = ChartTimeframe.allCases[index] == selectedTimeframebutton.tintColor = isSelected ? .systemBlue : .systemGraybutton.backgroundColor = isSelected ? .systemBlue.withAlphaComponent(0.1) : .clearbutton.layer.cornerRadius = 4}}@objc private func timeframeTapped(_ sender: UIButton) {selectedTimeframe = ChartTimeframe.allCases[sender.tag]}
}

五、架构设计建议

1、分层架构设计:

┌───────────────────────────────┐
│           UI Layer            │
│  - Chart Views (UIKit/SwiftUI)│
│  - Gesture Handlers           │
│  - View Controllers           │
└──────────────┬────────────────┘│
┌──────────────▼───────────────┐
│         Domain Layer         │
│  - Technical Indicators (MA, │
│    MACD, Bollinger Bands)    │
│  - Data Transformations      │
└──────────────┬───────────────┘│
┌──────────────▼───────────────┐
│         Data Layer           │
│  - Network Service (API)     │
│  - Database (CoreData/Realm) │
│  - Caching Mechanism         │
└──────────────────────────────┘

UI Layer:包含图表视图(UIKit/SwiftUI)、手势处理程序和视图控制器。
Domain Layer:包含技术指标(如MA、MACD、布林带)和数据转换。
Data Layer:包含网络服务(API)、数据库(CoreData/Realm)和缓存机制。
它们之间的关系是:UI层依赖于领域层,领域层依赖于数据层。

2、性能优化总结表:

优化点技术方案适用场景
大数据量分页加载 + 增量渲染历史数据加载
实时更新差异更新 + 增量绘制实时行情
复杂指标后台计算 + 缓存结果MACD/布林带等
流畅交互离屏渲染 + GPU加速手势缩放平移
内存优化对象复用 + 惰性加载移动设备限制

3、跨平台适配方案:

#if os(iOS)
import UIKit
typealias ViewRepresentable = UIView
#elseif os(macOS)
import AppKit
typealias ViewRepresentable = NSView
#endifclass CrossPlatformChartView: ViewRepresentable {#if os(iOS)override init(frame: CGRect) {super.init(frame: frame)commonInit()}#elseif os(macOS)override init(frame frameRect: NSRect) {super.init(frame: frameRect)commonInit()}#endifprivate func commonInit() {// 共享初始化代码}#if os(iOS)override func draw(_ rect: CGRect) {super.draw(rect)drawContent()}#elseif os(macOS)override func draw(_ dirtyRect: NSRect) {super.draw(dirtyRect)drawContent()}#endifprivate func drawContent() {// 共享绘制逻辑}
}

六、推荐方案选择

1、根据需求选择方案

需求场景推荐方案理由
简单展示SwiftUI + Canvas开发快,代码简洁
复杂交互UIKit + Core Graphics性能好,控制精细
多技术指标Charts 第三方库内置多种指标实现
跨平台SwiftUI + 条件编译支持iOS/macOS
实时行情Core Animation高效增量更新

2、性能对比表

方案10K数据点渲染手势流畅度内存占用开发效率
Core Graphics300ms⭐⭐⭐⭐⭐⭐⭐
SwiftUI Canvas800ms⭐⭐⭐⭐⭐⭐⭐⭐
Charts 库500ms⭐⭐⭐⭐中高⭐⭐⭐⭐⭐
Core Animation100ms⭐⭐⭐⭐⭐⭐⭐⭐

最终建议

  1. 对于专业交易应用,使用 Core Graphics + 自定义手势 实现最佳性能和体验
  2. 对于普通金融应用,使用 Charts 库 快速实现丰富功能
  3. 对于 SwiftUI 项目,使用 Canvas + 异步绘制 平衡性能与开发效率

股票图的实现需要平衡性能、功能和用户体验。建议从简单实现开始,逐步添加技术指标和交互功能,同时做好性能监控和优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/91415.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【unitrix】 6.4 数特征(number.rs)

一、源码 这段代码定义了一个名为Number的trait&#xff08;特质&#xff09;以及它的实现。 use crate::sealed::Sealed; use crate::number::{V, BaseNumber, TNumber};/// 数值的统一标记特质 /// 可以是编译时类型化数字(TNumber)或运行时变量(V<T>) pub trait Numbe…

AI治AI:大语言模型自检新法

“以火攻火”的思路解决大语言模型(LLMs)“幻觉”问题 虚构是由于与提示无关的内部因素而不可预测地从 LLM 中出现的幻觉。作者专注于衡量 LLM 对提示响应的不确定性,使用高不确定性表示虚构的假设。他们通过计算一个称为熵的量来估计这种不确定性**,熵可以被认为是模型生…

ESLint 配置错误:ReferenceError: prettier is not defined 解决方案

问题描述在使用 pnpm lint 运行 ESLint 时&#xff0c;出现以下错误&#xff1a;Oops! Something went wrong! :( ESLint: 9.31.0 ReferenceError: prettier is not defined该错误导致 ESLint 无法正确执行代码格式检查&#xff0c;但 不会影响项目的实际运行&#xff08;如 pn…

数据结构--准备知识

一.算法效率算法效率分为两种&#xff1a;第一种为时间效率&#xff0c;第二种为空间效率。时间效率称为时间复杂度&#xff0c;空间效率称为空间复杂度。时间复杂主要衡量一个算法的运行速度&#xff0c;空间复杂度主要衡量一个算法所需的 额外的空间&#xff08;现在不需要特…

HTML 入门教程:从零开始学习网页开发基础

一、HTML简介 1.1 什么是HTML&#xff1f; HTML全称是Hyper Text Markup Language&#xff08;超文本标记语言&#xff09;&#xff0c;由Tim Berners-Lee和同事Daniel W. Connolly于1990年创立。它是一种用于创建网页的标准标记语言&#xff0c;而不是编程语言。 1.2 HTML的…

使用 bat 批量创建带有项目前缀名的文件夹结构

在项目管理中&#xff0c;经常需要为每个新项目创建一套标准化的文件夹结构。如文档中所述&#xff0c;用户希望为每个项目&#xff08;如"Project 1"、“Project 2”&#xff09;创建以下结构的文件夹&#xff1a; project-1_export\project-1_DWG project-1_expo…

Python类中魔术方法(Magic Methods)完全指南:从入门到精通

文章目录Python类中魔术方法(Magic Methods)完全指南&#xff1a;从入门到精通一、魔术方法基础1. 什么是魔术方法&#xff1f;2. 魔术方法的特点二、常用魔术方法分类详解1. 对象创建与初始化2. 对象表示与字符串转换3. 比较运算符重载4. 算术运算符重载5. 容器类型模拟6. 上下…

H3CNE综合实验之五角星

H3CNE综合实验之五角星 实验拓扑图交换机地址规划表&#xff1a;SW6G1/0/1Vlan100:10.1.3.2/24G1/0/2Vlan90:10.1.4.2/24G1/0/3Vlan50:10.1.5.1/24G1/0/4Vlan60&#xff1a;10.1.6.1/24SW7G1/0/1Vlan50:10.1.5.2/24G1/0/2Vlan30:192.168.3.1/24G1/0/6Vlan70:10.1.1.2/24G1/0/3-…

Android EventBus使用方法与底层原理详解

EventBus 是什么&#xff1f; EventBus 是一个基于发布/订阅&#xff08;Publish/Subscribe&#xff09; 模式的开源库&#xff08;主要由 greenrobot 开发维护&#xff09;。它的核心目的是简化 Android 应用中不同组件&#xff08;如 Activity, Fragment, Service, Thread 等…

初等数论简明教程

初等数论简明教程 本文给出初等数论中的一些重要的定理与例题&#xff0c;证明风格采用 整除线法 与 命题节点法。 整除线法 指推理的第 nnn 步左边的字符可由前面左边的字符得到&#xff0c;右边的字符可由前面右边的字符得到&#xff0c;整除线变成了推理线&#xff0c;既少…

Spring之核心容器(IoC,DI,基本操作)详解

Spring之核心容器IoC/DI/基本操作详解一、核心概念&#xff1a;IoC与DI的本质1.1 IoC&#xff08;Inversion of Control&#xff0c;控制反转&#xff09;传统开发模式&#xff08;无IoC&#xff09;IoC模式&#xff08;Spring容器管理&#xff09;1.2 DI&#xff08;Dependenc…

【论文阅读】基于注意力机制的冥想脑电分类识别研究(2025)

基于注意力机制的冥想脑电分类识别研究&#x1f4a1; Meta DataTitle基于注意力机制的冥想脑电分类识别研究Authors周梓涵Pub. date2025&#x1f4dc; Research Background & Objective背景&#xff1a; 现代生活压力导致心理问题日益突出&#xff0c;冥想作为一种有效的心…

GitHub 上 Star 数量前 8 的开源 Web 应用项目

原文链接&#xff1a;https://www.nocobase.com/cn/blog/github-open-source-web-applications。 近期&#xff0c;我们发布了多篇「Top GitHub Star 开源项目推荐」系列文章&#xff0c;受到了大量点赞与收藏&#xff0c;很多开发者留言表示希望能看到更多不同领域的开源工具推…

FATFS文件系统原理及其移植详解

一、FATFS简介 FATFS 是一个完全免费开源的 FAT/exFAT 文件系统模块&#xff0c;专门为小型的嵌入式系统而设计。它完全用标准 C 语言&#xff08;ANSI C C89&#xff09;编写&#xff0c;所以具有良好的硬件平台独立性&#xff0c;只需做简单的修改就可以移植到 8051、PIC、A…

KubeRay 和 Ray

KubeRay 和 Ray 不是替代关系&#xff0c;而是互补的协作关系。两者在分布式计算生态中扮演不同角色&#xff0c;共同构成完整的云原生 AI 解决方案。以下是具体分析&#xff1a;&#x1f527; 1. 核心定位差异Ray 是分布式计算引擎&#xff0c;提供底层 API&#xff08;如 ray…

破解轮胎仓储高密度与柔性管理难题

轮胎作为特殊的大件异形工业品&#xff0c;其仓储管理长期面临多重挑战&#xff1a;规格型号繁杂导致SKU数量庞大&#xff0c;重型载重对货架承重提出极高要求&#xff0c;橡胶材质对防压变形、避光防老化等存储环境存在严苛标准。传统平置堆垛或普通货架方案不仅空间利用率不足…

EVA series系列(上)

目录 一、EVA 1、概述 2、方法 二、EVA-02 1、概述 2、架构 三、EVA-CLIP 1、概述 2、方法 四、EMU 1、概述 2、架构 3、训练细节 4、评估 一、EVA 1、概述 为探寻大规模表征学习任务的MIM预训练任务在ViT基础上扩展到1B参数量规模&#xff0c;结合10M级别&am…

ABP VNext + EF Core 二级缓存:提升查询性能

ABP VNext EF Core 二级缓存&#xff1a;提升查询性能 &#x1f680; &#x1f4da; 目录ABP VNext EF Core 二级缓存&#xff1a;提升查询性能 &#x1f680;引言 &#x1f680;一、环境与依赖 &#x1f6e0;️二、集成步骤 ⚙️2.1 安装 NuGet 包2.2 注册缓存服务与拦截器2…

3.1k star!推荐一款开源基于AI实现的浏览器自动化插件工具 !

大家好&#xff01;今天&#xff0c;我要给大家介绍一款超实用的开源工具——Chrome MCP Server&#xff01;这款工具不仅能大幅提升我们的工作效率&#xff0c;还能让AI助手&#xff08;如Claude&#xff09;直接操控浏览器&#xff0c;实现自动化操作、内容分析等强大功能。 …

关于 OpenAI 的反思

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…