HotXLS compila fórmulas escritas en notación de referencia R1C1 a través de TXLSCalculator.GetCompiledFormulaR1C1, que acepta formas como R2C3 (absoluta), R[-1]C[2] (desplazamiento relativo) y RC (la celda actual), las convierte a notación A1 según la fila y la columna de la celda donde reside la fórmula, y envía el resultado al mismo compilador que gestiona las fórmulas A1 ordinarias. Para código de Delphi y C++Builder que genera la misma fórmula en cientos de filas, ese único método elimina toda una categoría de errores de concatenación de cadenas
Esta categoría de errores resulta familiar para cualquiera que haya rellenado una columna mediante programación. Se itera sobre las filas y, para cada fila, se construye una cadena de fórmula A1 mediante Format('D%d*E%d', [Row, Row]). Cada iteración inserta números de fila en el texto, siendo estos la única parte que cambia. Si se equivoca en el desplazamiento una sola vez, si mezcla un contador de bucle basado en 0 con números de fila A1 basados en 1, o si desplaza el bloque de datos por una fila de encabezado, cada fórmula de la columna apuntará una fila desviada. No ocurre ningún fallo evidente; los números simplemente son incorrectos. La fórmula que realmente deseaba implementar, "multiplicar las dos celdas a mi izquierda", no menciona ningún número de fila, y la notación R1C1 permite escribirla de esa forma
¿Qué es la notación R1C1 y cuándo se debe utilizar?
La notación R1C1 direcciona las celdas por su número de fila y columna en lugar de por la letra de columna más el número de fila, y marca las referencias relativas como desplazamientos explícitos desde la celda de la fórmula. R2C3 es la celda absoluta en la fila 2, columna 3, que la notación A1 escribe como $C$2. R[-1]C[2] es una fila arriba y dos columnas a la derecha de donde se ubique la fórmula. RC representa la celda de la fórmula misma. Los desplazamientos entre corchetes son la clave: una referencia relativa en R1C1 se lee de la misma manera sin importar qué celda la contenga, mientras que la representación A1 de esa misma referencia cambia con cada fila
La notación demuestra su valor en un escenario muy específico, que además es común: la generación de plantillas, donde la misma fórmula relativa debe aplicarse a cada fila de una región de datos. En A1, debe volver a generar el texto de la fórmula para cada fila. En R1C1, el texto es una constante. Esto también es más afín a la manera en que los formatos de archivos de hojas de cálculo piensan internamente: los registros de fórmulas compartidas almacenan las referencias relativas como desplazamientos de fila y columna desde la celda host, por lo que una cadena A1 por fila es algo que su código sintetiza solo para que el analizador la descomponga de nuevo en desplazamientos. R1C1 evita ese viaje de ida y vuelta. Para las fórmulas interactivas creadas por humanos, A1 sigue siendo la elección natural, razón por la cual se mantiene como la opción predeterminada en todo HotXLS
¿Cómo compila HotXLS una fórmula R1C1?
HotXLS expone la característica en dos niveles, añadida en la versión v2.175.0. TXLSCalculator.GetCompiledFormulaR1C1(UncompiledFormula: String; SheetID, CurRow, CurCol: Integer): TXLSCompiledFormula es la llamada más habitual: devuelve una fórmula compilada lista para su evaluación, exactamente igual que su equivalente A1 GetCompiledFormula, pero con dos parámetros adicionales que indican la fila y columna (basadas en 0) de la celda a la que pertenece la fórmula. Por debajo, TXLSFormula.GetCompiledR1C1 produce el árbol de sintaxis pura, y una función independiente R1C1ToA1(const AFormula: String; CurRow, CurCol: Integer): String realiza la conversión de notación propiamente dicha. La canalización es sumamente simple: traduce el texto R1C1 al texto A1 equivalente utilizando las coordenadas de la celda host y luego compila el texto A1 mediante el motor existente; el mismo motor que resuelve los nombres definidos y las referencias entre hojas y despacha las funciones de hoja de trabajo personalizadas
Como la conversión ocurre antes de la compilación, todo el proceso posterior se comporta como si hubiera escrito la fórmula A1 usted mismo. R1C1ToA1('R2C3', 4, 3) devuelve '$C$2' independientemente de la celda host, debido a que ambas coordenadas son absolutas. R1C1ToA1('SUM(R[-3]C[0]:R[-1]C[0])', 4, 3) (una fórmula ubicada en D5, ya que CurRow = 4 y CurCol = 3 se basan en 0) devuelve 'SUM(D2:D4)': los desplazamientos se resolvieron frente a la fila 5, columna D, y se emitieron como referencias A1 relativas simples. Los rangos no requieren un manejo especial; los dos puntos pasan directamente y cada extremo se convierte de forma independiente
// The formula lives in D5: CurRow = 4, CurCol = 3 (both 0-based)
S := R1C1ToA1('R2C3', 4, 3);
// S = '$C$2' (absolute row and column)
S := R1C1ToA1('SUM(R[-3]C[0]:R[-1]C[0])', 4, 3);
// S = 'SUM(D2:D4)' (offsets resolved against D5)
S := R1C1ToA1('ROUND(R[-1]C[0], 2)', 4, 3);
// S = 'ROUND(D4, 2)' (the R in ROUND is left alone)
Rellenar una columna con una sola fórmula relativa
El beneficio se hace evidente en el bucle. Compare la versión A1, que vuelve a generar el texto de la fórmula en cada iteración, con la versión R1C1, donde la fórmula es una constante y solo se mueven las coordenadas del host. Ambas se compilan a través de TXLSCalculator y se evalúan con GetValue; el calculador recibe un callback de proveedor de celdas en su construcción para que el evaluador pueda extraer los valores de las celdas desde su fuente de datos
// A1 style: a different formula string for every row
for Row := 1 to 500 do
begin
FormulaText := Format('D%d*E%d', [Row + 1, Row + 1]); // 1-based A1 rows
Compiled := Calc.GetCompiledFormula(FormulaText, 0);
// ... evaluate, store, free ...
end;
const
AmountFormula = 'RC[-2]*RC[-1]'; // two cells to the left, same row
var
Calc: TXLSCalculator;
Compiled: TXLSCompiledFormula;
Value: Variant;
Row: Integer;
begin
Calc := TXLSCalculator.Create(nil, Provider.GetValue);
try
for Row := 1 to 500 do
begin
Compiled := Calc.GetCompiledFormulaR1C1(AmountFormula, 0, Row, 5);
try
if Calc.GetValue(0, Compiled, Row, 5, Value, 1) = lxOk then
StoreResult(Row, 5, Value);
finally
Compiled.Free;
end;
end;
finally
Calc.Free;
end;
end;
El bucle R1C1 no tiene ninguna operación aritmética de fila en el texto de la fórmula. 'RC[-2]*RC[-1]' significa "misma fila, dos columnas a la izquierda, por misma fila, una columna a la izquierda" tanto en la fila 2 como en la fila 500, y si el bloque de datos se desplaza posteriormente hacia abajo debido a una fila de encabezado, la constante de la fórmula no cambia; solo cambian los límites del bucle. La versión A1 tiene dos lugares donde el ajuste + 1 puede ser incorrecto; la versión R1C1 tiene cero
¿Qué formas de R1C1 acepta el convertidor?
El convertidor R1C1ToA1 reconoce las formas documentadas para la versión v2.175.0: R[n]C[m] para desplazamientos relativos en cualquier dirección, RnCm para fila y columna absolutas, R[-n]C[m] con desplazamientos negativos, RC simple para la propia celda de la fórmula y rangos como R1C1:R3C3 o R[-1]C:R[1]C. Las partes de fila y columna se analizan de forma independiente, por lo que las formas mixtas como R[1]C3 (fila relativa, columna absoluta) también funcionan, y las letras no distinguen entre mayúsculas y minúsculas, por lo que r[-1]c[2] se compila igual que su gemelo en mayúsculas. Las partes entre corchetes se convierten en coordenadas A1 no ancladas (relativas); los números simples se convierten en coordenadas absolutas con anclaje $
La pregunta interesante es cómo el convertidor evita alterar todo lo demás en la fórmula, ya que R y C son letras comunes. Su regla de discriminación se basa en tokens: una letra R solo se considera el inicio de una referencia cuando no va precedida de otra letra, y luego el candidato debe analizarse por completo (parte de fila opcional, C obligatoria, parte de columna opcional) o de lo contrario el texto se restaura intacto. Por eso, ROUND(R[-1]C[0], 2) convierte solo la referencia interna: la R en ROUND va seguida de O en lugar de un dígito, corchete o C, por lo que el análisis falla y el nombre de la función se mantiene tal cual. La misma lógica protege a ROW(), y los nombres de funciones que comienzan con C nunca son candidatos, dado que solo la letra R inicia una referencia. Las notas de la versión v2.175.0 estableceren que los literales de cadena y los identificadores también se omiten; aun así, si un literal entre comillas en su fórmula contiene texto con la forma exacta de una referencia R1C1, vale la pena verificar el resultado compilado una vez antes de confiar en él en producción
Bases de coordenadas y detalles de anclaje que vale la pena conocer
Dos convenciones se encuentran dentro de esta API, y mantenerlas claras evita el único problema real. Los parámetros CurRow and CurCol de GetCompiledFormulaR1C1 se basan en 0, siguiendo la API del calculador, mientras que los números dentro de la propia notación se basan en 1, coincidiendo con lo que muestra Excel: R2C3 es la fila 2, columna 3, que es $C$2 y no $D$3. Si el contador de su bucle ya se basa en 0, lo pasa directamente como CurRow; el ajuste + 1 reside dentro del convertidor, no en su código
Un detalle más es importante si la fórmula compilada sobrevive a la celda para la cual fue compilada. Cuando se omite una parte de fila o columna (las formas RC[-1] o R[2]C), la coordenada omitida se resuelve hacia la celda host y se emite como una coordenada absoluta con anclaje $ en el texto A1 convertido. En el momento de la evaluación esto es invisible, ya que el valor es el mismo en ambos casos. Pero el anclaje relativo frente al absoluto decide cómo se desplazan las referencias cuando se insertan o eliminan filas o columnas posteriormente, tal como se analiza en el artículo complementario sobre el ajuste de referencias de fórmulas. Si necesita que una coordenada se mantenga relativa ante ediciones estructurales, escriba el desplazamiento de forma explícita (R[0]C[-1] en lugar de RC[-1]) para que el convertidor emita una referencia A1 sin anclaje
La compilación R1C1 forma parte del motor de fórmulas en el Componente HotXLS Delphi Excel, junto con el compilador A1, el gráfico de recálculo y la API de evaluación mostrados anteriormente. Si su código construye hojas de cálculo mediante el bucle de fórmulas por las columnas, mover esos bucles de un esquema A1 unido por cadenas a una constante R1C1 única representa una de las mejoras de confiabilidad más sencillas disponibles