Shiny应用Ngram预测下一词报错:closure对象不可子集化
Fixing the "object of type 'closure' is not subsettable" Error in Your Ngram Predictor
Hey there! Let's tackle that frustrating error you're hitting with your next-word prediction functions for Shiny. The root causes are mostly incorrect empty-value checks and misusing return() inside ifelse—let's fix these issues one by one.
Key Issues in Your Code
- Wrong empty output check:
out == "character(0)"doesn't work because when no matches are found,as.character()returns a length-0 character vector, not the string"character(0)". - Misusing
return()inifelse:ifelseis a vectorized function, and puttingreturn()inside it treatsreturnas a closure (function object) instead of executing it, which triggers the subsettable error. We'll replaceifelsewith standardif/elsestatements for clearer, error-free logic. - Minor dataframe handling bug: In the
ngramsfunction, you're callingstr_count(input, boundary("word"))on the dataframe instead of thetextcolumn.
Corrected Code
Let's rewrite each function with these fixes:
library(tidyverse) library(stringr) library(dplyr) library(ngram) library(tidyr) # Load pre-built ngram tables bi_words <- readRDS("./bi_words.rds") tri_words <- readRDS("./tri_words.rds") quad_words <- readRDS("./quad_words.rds") bigram <- function(input_words){ num <- length(input_words) out <- bi_words %>% dplyr::filter(word1 == input_words[num]) %>% top_n(1, n) %>% slice(1) %>% # Simpler than filter(row_number() == 1L) select(num_range("word", 2)) %>% as.character() # Check if output is empty, return "?" if true if (length(out) == 0) { return("?") } else { return(out) } } trigram <- function(input_words){ num <- length(input_words) out <- tri_words %>% dplyr::filter(word1 == input_words[num-1], word2 == input_words[num]) %>% top_n(1, n) %>% slice(1) %>% select(num_range("word", 3)) %>% as.character() # Fall back to bigram if no trigram match if (length(out) == 0) { return(bigram(input_words)) } else { return(out) } } quadgram <- function(input_words){ num <- length(input_words) out <- quad_words %>% dplyr::filter(word1 == input_words[num-2], word2 == input_words[num-1], word3 == input_words[num]) %>% top_n(1, n) %>% slice(1) %>% select(num_range("word", 4)) %>% as.character() # Fall back to trigram if no quadgram match if (length(out) == 0) { return(trigram(input_words)) } else { return(out) } } ngrams <- function(input){ input_df <- data.frame(text = input, stringsAsFactors = FALSE) replace_reg <- "[^[:alpha:][:space:]]*" input_clean <- input_df %>% mutate(text = str_replace_all(text, replace_reg, "")) %>% mutate(text = tolower(text)) input_words <- unlist(str_split(input_clean$text, boundary("word"))) input_count <- length(input_words) # More direct than str_count # Choose the right ngram function based on word count out <- case_when( input_count == 1 ~ bigram(input_words), input_count == 2 ~ trigram(input_words), input_count >= 3 ~ quadgram(input_words), TRUE ~ "?" # Fallback for empty input ) return(out) } # Test the function input <- "In case of a" ngrams(input)
What Changed?
- Empty output check: Swapped
out == "character(0)"forlength(out) == 0to correctly detect empty results. - Replaced
ifelsewithif/else: This avoids the closure error and makes the fallback logic easier to read. - Simplified row selection: Used
slice(1)instead offilter(row_number() == 1L)for cleaner code. - Fixed dataframe handling: In
ngrams, we now target thetextcolumn explicitly and uselength(input_words)instead ofstr_countfor more reliable word count. - Added fallback for empty input: The
case_whenincludes aTRUEclause to return "?" if the input is empty.
Now your prediction functions should work without the closure error, and the fallback logic (quadgram → trigram → bigram → "?") will behave as expected.
内容的提问来源于stack exchange,提问作者Jayashree K
相关产品推荐
相关产品推荐

