Shiny中actionButton触发递归函数引发C栈溢出错误求助
解决Shiny应用中Gauss-Jordan消元的无限递归问题
你遇到的C stack usage too close to the limit错误确实是无限递归导致的——问题出在你重复定义了同名的mat_list对象,而且第二个eventReactive内部又调用了mat_list(),这就形成了循环调用:每次触发play按钮,eventReactive会调用自己,不断递归直到栈溢出。
要解决这个问题,我们需要用可变的响应式状态容器(reactiveVal)来追踪当前的矩阵状态,而不是用覆盖式的reactive/eventReactive定义。下面是具体的调整方案:
核心思路
- 用
reactiveVal保存当前的变换矩阵A和单位矩阵I的状态,这个容器可以被动态更新。 - 监听用户修改输入矩阵或切换维度的事件,重置状态容器的初始值。
- 监听
play按钮的点击事件,取出当前状态,执行一步Gauss-Jordan消元,然后更新状态容器。 - 让
renderTable依赖这个状态容器,这样无论是输入变化还是点击按钮,表格都会自动更新。
修正后的完整代码
library(shiny) library(shinythemes) library(shinyMatrix) library(plotly) library(stringr) # 假设你的apply_gauss_jordan_3d函数已经实现(示例逻辑) apply_gauss_jordan_3d <- function(A, I) { # 这里写实际的一步消元逻辑,示例仅做简单演示 if (A[1,1] == 0) { # 交换行(示例操作) temp_row <- A[1,] A[1,] <- A[2,] A[2,] <- temp_row temp_row_I <- I[1,] I[1,] <- I[2,] I[2,] <- temp_row_I } else { # 归一化第一行(示例操作) factor <- A[1,1] A[1,] <- A[1,] / factor I[1,] <- I[1,] / factor } list(A, I) } # Create user interface ui <- fluidPage( theme = shinytheme("cerulean"), titlePanel( headerPanel("Linear Transformations") ), withMathJax(), sidebarLayout( sidebarPanel( radioButtons( "dimension", label = "Choose number of dimensions", choices = c("2D", "3D"), selected = "2D" ), matrixInput( "transform_mat", label = "Input the transformation matrix", value = matrix(c(1, 0, 0, 1), nrow = 2, ncol = 2, byrow = FALSE), rows = list(names = FALSE), cols = list(names = FALSE), class = "numeric", copy = TRUE, paste = TRUE ) ), mainPanel( tabsetPanel( type = "tabs", tabPanel("Plot", plotlyOutput("plot")), tabPanel("Null Space", verbatimTextOutput("null_space"), plotlyOutput("null_space_plot")), tabPanel("Determinant", uiOutput("determinant")), tabPanel( "Gauss-Jordan", tableOutput("gauss_matrix"), tableOutput("gauss_identity"), actionButton("rewind", "", icon = icon("fast-backward")), actionButton("play", "", icon = icon("play")), actionButton("advance", "", icon = icon("fast-forward")) ) ) ) ) ) # Define server function server <- function(input, output, session) { # 初始化响应式状态容器,保存当前的A和I矩阵 current_mats <- reactiveVal() # 监听输入变化(矩阵修改/维度切换),重置状态 observeEvent(c(input$dimension, input$transform_mat), { dim <- as.numeric(str_remove(input$dimension, "D")) initial_A <- input$transform_mat initial_I <- diag(dim) current_mats(list(initial_A, initial_I)) }, ignoreInit = FALSE) # ignoreInit=FALSE确保初始化时就触发 # 监听play按钮,执行一步消元并更新状态 observeEvent(input$play, { # 获取当前状态 current <- current_mats() current_A <- current[[1]] current_I <- current[[2]] # 执行一步消元(注意这里要传入正确的A和I,之前你的代码传了两次A) new_mats <- apply_gauss_jordan_3d(current_A, current_I) # 更新状态容器 current_mats(new_mats) }) # 渲染变换矩阵表格 output$gauss_matrix <- renderTable({ current_mats()[[1]] }, colnames = FALSE) # 渲染单位矩阵(消元后的逆矩阵)表格 output$gauss_identity <- renderTable({ current_mats()[[2]] }, colnames = FALSE) # 其他模块的代码(Plot/Null Space/Determinant)可以保留原逻辑 } # Run shiny application shinyApp(ui, server)
关键调整说明
- 用
reactiveVal替代重复的mat_list:current_mats是一个可变的响应式变量,专门用来保存当前的矩阵状态,避免了递归调用的问题。 - 修正
apply_gauss_jordan_3d的参数:之前你错误地传入了两次变换矩阵,现在改为传入当前的A和I矩阵,符合函数的输入要求。 - 明确状态触发逻辑:用户修改输入时重置状态,点击按钮时更新状态,两个逻辑完全分离,不会互相干扰。
这样调整后,你的应用就能正常响应输入变化和按钮点击,不会再出现无限递归导致的栈溢出问题了。
内容的提问来源于stack exchange,提问作者ramonmb
相关产品推荐
相关产品推荐

