Mask/CLIP Raster images with different resolution in IDL - idl-programming-language

I want to clip/mask raster image (500meters resolution) by another raster images (10 km resolution) using IDL Programming after the clip/mask process image should be in 500 meters resolution. I have 365 images pair and I want to a process by IDL programming. Can anybody write this code in IDL?
Thanks in advance.

#MrFuppes
Initially, I write one code in IDL which able to mask/clip the same resolution images because those are in same dimension.
But I cannot write the mask function for different resolution where is the problem in dimension and resolution.
In my code:
Band 1(500 meters) is my base images and I want to mask this image by cloud mask image(500 meters)(It works well) and then I want to do another mask by AOD_MASK images(10 km) resolution.
But how to write MASK function for different dimension images I have no idea.
If you have any suggestion regarding this issue then, please suggested me.
Here I attached my test DATA please check it.
Below is my IDL code:
`Pro MASK
COMPILE_OPT idl2
ENVI, /RESTORE_BASE_SAVE_FILES
ENVI_BATCH_INIT, /NO_STATUS_WINDOW
print, ' ' + 'Batch Processing Start'
print, ' ' + systime()
;********************************************************************************
;Input location of different file
InputAOD = 'F:\DB_test_data\VAR2\TEST_DATA\Clip\'
Input_cloud = 'F:\DB_test_data\VAR2\TEST_DATA\Clip\'
Input_TOA_B1 = 'F:\DB_test_data\VAR2\TEST_DATA\Clip\'
;Output location
Output = 'F:\DB_test_data\OUT_PUT\'
; Search for inpiut data
FileCloud = File_search(Input_cloud + "*Aerosol_Cldmask_Land_Ocean-
Aerosol_Cldmask_Land_Ocean.tif",COUNT=Count_in1)
FileAOD = File_Search(InputAOD + '*Corrected_Optical_Depth_Land_2-
Corrected_Optical_Depth_Land.tif',COUNT=count_in)
FileBAND1 = File_Search(Input_TOA_B1 + '*pssgrp_000501330352.EV_500_RefSB_1-
EV_500_RefSB.tif',COUNT=COUNT)
nfiles = n_elements(count_in1)
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$
FOR k=0, nfiles-1 DO BEGIN
Print, 'Processing File =', k +1
Print, 'Total Files=', count_in1
string_count = strtrim(nfiles,2)
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;cloud(500_METER)
ENVI_OPEN_FILE, FileCloud[k], r_FiD=FiD_cld
IF (FiD_cld EQ -1) then return
ENVI_FILE_QUERY, FiD_cld, dims=dims, nb=nb, ns=cld_ncols, nl=cld_nrows
pos = [0]
map_info = envi_get_map_info(FiD=FiD_cld)
Cloud = Double(ENVI_GET_DATA(FiD=FiD_cld, dims=dims, pos=[0]))
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;BAND_1(500_METER)
envi_open_file,FileBAND1[k],r_fid=fid_T_B1
if (fid_T_B1 eq -1)then return
envi_file_query,fid_T_B1,dims=dims,nb=nb, ns=toa_B1_ncols, nl=toa_B1_nrows
pos=[0]
map_info1=envi_get_map_info(fid=fid_T_B1)
layer1 = fltarr(toa_B1_ncols,toa_B1_nrows,nb)
FOR i=0,nb-1 DO BEGIN
layer1[*,*,i]=ENVI_GET_DATA(fid=fid_T_B1,dims=dims,pos=pos[i])
ENDFOR
;To replace 65535 with NAN
i=WHERE(layer1 eq 65535,count)
if (count gt 0)then layer1[i]=!VALUES.F_NAN
TOA_B1=layer1*0.000053811826d
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;AOD(10KM_RESOLUTION)
ENVI_OPEN_FILE, FileAOD[k], r_FiD=FiD_AOD
IF (FiD_AOD EQ -1) then return
ENVI_FILE_QUERY, FiD_AOD, dims=dims, nb=nb, ns=aod_ncols, nl=aod_nrows
pos = [0]
map_info = envi_get_map_info(FiD=FiD_AOD)
layer2 = fltarr(aod_ncols,aod_nrows,nb)
FOR i=0,nb-1 DO BEGIN
layer2[*,*,i]=ENVI_GET_DATA(fid=FiD_AOD,dims=dims,pos=pos[i])
ENDFOR
;To replace -9999 with NAN
i=WHERE(layer2 eq -9999,count)
if (count gt 0)then layer2[i]=!VALUES.F_NAN
AOD=layer2*0.0010000000474974513d
;set the specific range for mask
AOD_MASK = 1.0*(AOD GE 0.0 and AOD LE 0.1) + (AOD GT 0.1)*0.0
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
; to get same dimensions for B1TOA and CLD data
if (cld_ncols le toa_B1_ncols) then begin
xsize = cld_ncols
endif else begin
xsize = toa_B1_ncols
endelse
if (cld_nrows le toa_B1_nrows) then begin
ysize = cld_nrows
endif else begin
ysize = toa_B1_nrows
endelse
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
; to call parameters again with same dims
Cloud = FSC_Resize_Image(Cloud,xsize, ysize)
Band1 = FSC_Resize_Image(TOA_B1,xsize, ysize)
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;First masking processes with cloud mask images(500 meters)
Cloud_masked = Cloud * Band1
;To replace 0 with NAN
i=WHERE(Cloud_masked eq 0,count)
if (count gt 0)then Cloud_masked[i]=!VALUES.F_NAN
Cloud_masked_Band1=Cloud_masked
;Second masking processes with AOD_MASK images(10KM)
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
; Define output FileName
FileName = STRJOIN(STRSPLIT(STRMID(FILE_BASENAME(FileBAND1[k], '.tif'), 0, 75), '.', /EXTRACT), '_')
print, FileName
; write output File
out_name_out = Output + FileName +'_masked_image.tif'
map_info = envi_get_map_info(FiD=fid_T_B1)
ENVI_WRITE_ENVI_FILE, Cloud_masked, out_name=out_name_out, map_info=map_info
;FileName = STRJOIN(STRSPLIT(STRMID(FILE_BASENAME(FileAOD[k], '.tif'), 0, 75), '.', /EXTRACT), '_')
;print, FileName
; write output File
;out_name_out = Output + FileName +'_AOD.tif'
;map_info = envi_get_map_info(FiD=FiD_AOD)
;ENVI_WRITE_ENVI_FILE, AOD,out_name=out_name_out, map_info=map_info
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
Print, FileName + ' ' + strtrim(k+1,2) + ' of ' + string_count + ' Processed'
Endfor
Print, ' ' + systime()
Print, ' ' + 'Batch Processing End'
End
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
FUNCTION FSC_Resize_Image, image, cols, rows, $
INTERPOLATE=interp, $
MINUS_ONE=minus_one
Compile_Opt idl2
; Check parameters.
IF N_Params() EQ 0 THEN BEGIN
Print, 'USE SYNTAX: FSC_Resize_Image, image, cols, rows'
RETURN, "FSC_Resize_Image Syntax Error"
ENDIF
; Return to the caller on an error.
On_Error, 2
; Check image parameter for size. Only 2D and 3D images allowed.
ndim = SIZE(image, /N_DIMENSIONS)
dims = SIZE(image, /DIMENSIONS)
IF ((ndim LT 2) OR (ndim GT 3)) THEN $
Message, 'Input image must have two or three dimensions.'
; Check for keywords. Default for minus_one is 0.
interp = Keyword_Set(interp)
IF N_Elements(minus_one) EQ 0 THEN minus_one = 0
m1 = Keyword_Set(minus_one)
; 2D images are immediately passed to IDL's Congrid command, with
; the CENTER keyword set.
IF ndim EQ 2 THEN BEGIN
RETURN, Congrid(image, cols, rows, CENTER=1, MINUS_ONE=minus_one, $
INTERP=interp)
ENDIF
; 24-bit images are handled differently. The "3" dimension of a 24-bit
; image should not be interpolated.
offset = 0.5 ; To center the pixel location (i.e., CENTER=1 for Congrid)
index3 = Where(dims EQ 3)
CASE index3 OF
0: BEGIN
srx = [0,1,2]
sry = Float(dims[1] - m1) / (cols - m1) *(Findgen(cols) + offset) - offset
srz = Float(dims[2] - m1) / (rows - m1) *(Findgen(rows) + offset) - offset
END
1: BEGIN
srx = Float(dims[0] - m1) / (cols - m1) *(Findgen(cols) + offset) - offset
sry = [0,1,2]
srz = Float(dims[2] - m1) / (rows - m1) *(Findgen(rows) + offset) - offset
END
2: BEGIN
srx = Float(dims[0] - m1) / (cols - m1) *(Findgen(cols) + offset) - offset
sry = Float(dims[1] - m1) / (rows - m1) *(Findgen(rows) + offset) - offset
srz = [0,1,2]
END
ENDCASE
; Do the interpolation. Preserve nearest neighbor sampling, if required.
IF interp THEN BEGIN
RETURN, Interpolate(image, srx, sry, srz, /GRID)
ENDIF ELSE BEGIN
RETURN, Interpolate(image, Round(srx), Round(sry), Round(srz), /GRID)
ENDELSE
END`

Related

95% significance plot with NCL

i want to plot a contour of PBLH difference between 2 wrf-chem simulations. I have the netcdf means (attached files), and i want to draw contour of 95% significance levels, but the script did not work, can you give your suggestions please?
"Error: scalar_field: If the input data is 1-dimensional, you must set sfXArray and sfYArray to 1-dimensional arrays of the same length.
warning:create: Bad HLU id passed to create, ignoring it"
i'm explecting a contour plot with Grey shaded areas indicate regions with less than 95 % significance.
here is the code. You can test it with any two WRF netcdf files:
`;----------------------------------------------------------------------
; contoursym_1.ncl
;
; Concepts illustrated:
; - Using a symmetric color map
; - Using a blue-red color map
; - Explicitly setting contour levels
;----------------------------------------------------------------------
;
; These files are loaded by default in NCL V6.2.0 and newer
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_code.ncl"
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_csm.ncl"
load "$NCARG_ROOT/lib/ncarg/nclscripts/wrf/WRF_contributed.ncl"
; This file still has to be loaded manually
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/shea_util.ncl"
begin
;*****************
;-- load data
;*****************
;specify file names (input&output, netCDF)
pathin = "./" ; directory
fin1 = "15-25-omet.nc" ; input file name #1
fin2 = "15-25-wrfda.nc" ; input file name #2
fout = "signif_pblh_omet-wrfda" ; output file name
foutnc = fout+".nc"
f = addfile ("15-25-omet.nc", "r")
; open input files
in1 = addfile(pathin+fin1,"r")
in2 = addfile(pathin+fin2,"r")
; read data
tmp1 = in1->PBLH
tmp2 = in2->PBLH
x = f->PBLH(0,:,:)
diff=tmp1-tmp2
printVarSummary(tmp1)
printVarSummary(tmp2)
;****************************************************
; calculate probabiliites
;****************************************************
;t-test
res1=True
xtmp=tmp1(XTIME|:,south_north|:, west_east|:)
ytmp = tmp2(XTIME|:,south_north|:, west_east|:)
aveX = dim_avg_Wrap(xtmp)
aveY = dim_avg_Wrap(ytmp)
varX = dim_variance(xtmp)
varY = dim_variance(ytmp)
sX = dimsizes(xtmp&XTIME)
sY = dimsizes(ytmp&XTIME)
print(sX)
print(sY)
alphat = 100.*(1. - ttest(aveX,varX,sX, aveY,varY,sY, True, False))
;aveX = where(alphat.lt.95.,aveX#FillValue, aveX)
;print(alphat)
;*********************
;---Start the graphics
;**********************
wks = gsn_open_wks("ps" ,"Bias_gray_F") ; ps,pdf,x11,ncgm,eps
res = True
res#gsnMaximize = True ; uncomment to maximize size
res#gsnSpreadColors = True ; use full range of colormap
res#cnFillOn = True ; color plot desired
res#cnLinesOn = False ; turn off contour lines
res#cnLineLabelsOn = True ; turn off contour labels
res#cnLineLabelsOn = True ; turn on line labels
res#lbOrientation = "Vertical"
res#lbLabelPosition = "Right" ; label position
res#tiMainFontHeightF = 0.025
res#lbBoxEndCapStyle = "TriangleBothEnds" ; triangle label bar
;************************************************
; Use WRF_contributed procedure to set map resources
;************************************************
res = True
WRF_map_c(f, res, 0) ; reads info from file
;************************************************
; if appropriate, set True for native mapping (faster)
; set False otherwise
;************************************************
res#tfDoNDCOverlay = True
;************************************************
; associate the 2-dimensional coordinates to the variable for plotting
; only if non-native plot
;************************************************
if (.not.res#tfDoNDCOverlay) then
x#lat2d = f->XLAT(0,:,:) ; direct assignment
x#lon2d = f->XLONG(0,:,:)
end if
;************************************************
; Turn on lat / lon labeling
;************************************************
res#pmTickMarkDisplayMode = "Always" ; turn on tickmarks
;res#tmXTOn = False ; turn off top labels
;res#tmYROn = False ; turn off right labels
;************************************************
; Loop over all times and levels ( uncomment )
; Demo: one arbitrarily closen time and level
;************************************************
dimx = dimsizes(x) ; dimensions of x
ntim = dimx(0) ; number of time steps
klev = dimx(1) ; number of "bottom_top" levels
nt = 0 ; arbitrary time
kl = 6 ; " level
opt=True
opts=True
res1=True
res = opts ; Use basic options for this field
opts#MainTitle = "OMET-FBDA"
opts#InitTime = False ; Do not plot time or footers
opts#Footer = False
plot0 = gsn_csm_contour_map(wks,diff(0,:,:),res ) ; define plot 0
pval = gsn_csm_contour(wks,alphat(0,:),res1) ;-- this adds a contour line around the stippling
opt#gsnShadeMid="gray62"
pval = gsn_contour_shade(pval,0.05,1.00,opt) ;-- this adds the stippling for all pvalues <= 0.05
overlay(plot0,pval)
draw(plot0)
frame(wks)
end`

Getting a zero value for a variable that clearly should not be zero

I have written the following code to calculate temperature distribution along a fin. For some reason, it keeps calculating certain values as zero, even when they aren't!
for t = 0:300:3000
for i = 2:L2
if i ==2
A(i)= 0.75*rhocp*DX/DT + 3*k/dx;
B(i)= k/dx;
C(i)= 2*k/dx;
D(i)= 0.75*rhocp*(DX/DT)*T(i);
elseif i == L2
A(i)= 0.75*rhocp*DX/DT + 3*k/dx;
B(i)= 2*k/dx;
C(i)= k/dx;
D(i)= 0.75*rhocp*(DX/DT)*T(i);
else
A(i)= 0.75*rhocp*DX/DT + 2*k/dx;
B(i)= k/dx;
C(i)= k/dx;
D(i)= rhocp*(DX/DT)*T(i);
end
P(1) = 0;
Q(1) = T(1);
for i = 2:L2
DENO = A(i) - C(i)*P(i-1);
NUM = D(i)+ C(i)*Q(i-1);
P(i) = B(i)/DENO;
Q(i) = NUM/DENO;
end
for i =L2:-1:2
if i == L2
T(i) = Q(i);
else
T(i) = P(i)*T(i+1) + Q(i);
end
end
T(L1) = ((2*k*T(L2) - h*DX*Tinf)/(2*k - h*DX));
end
disp (T);
`
DENO and NUM is calculated as zero in the first iteration, even though on calculating their values is not zero! This leads to "Division by zero" error.
A(2)-C(2)*P(1)
ans =
3750.
Analytically it has got a value though.
Please give the parameter values so one can run your script...
DT was missing , i set it to 1 .
Finally I found the problem: you forgot an end to close the loop
for i = 2:L2
the end has to be put just before P(1)=0
Moreover this loop does not depend on the t value so it could be put before the loop for t = 0:300:3000

Classic ASP : what's wrong with my code to convert quantity from dozen to piece (eg. 10.3 dozen = 123 pieces)

What i want is to retrieve quantity in database from piece and covert it to dozen. Then input as dozen and convert back to pieces and save to database again.
when I input data eg. 10.3, it should convert to 123 piece for me ((10 * 12) + 3). My code work well without my "If clause" but only when data was "single" type. It made error when I input integer number, so I added "If.." statement to check it first which is now the output was correct for Integer but incorrect when I input single number.
I have this code..
Function DzToPcs(val)
'If CLng(val) = val then <-- not work
'if Fix(val) <> val then <-- work but the output was not correct when input single type number.
if Int(vInt) = vInt then <-- work but the output was not correct when input single type number.
DztoPcs = val * 12
else
strInt = Cstr(val)
a = Split(strInt,".")
dz = a(0)
pcs = a(1)
getdz = Cint(dz)
getpcs = Cint(pcs)
DztoPcs = (getdz * 12) + getpcs
end if
I'm not sure what's wrong with your if statements (my VBScript is a little rusty), but you could try this alternative:
Function DzToPcs(val)
strInt = Cstr(val)
a = Split(strInt,".")
dz = a(0)
if UBound(a) > 0 then
pcs = a(1)
getdz = Cint(dz)
getpcs = Cint(pcs)
DztoPcs = (getdz * 12) + getpcs
else
DztoPcs = dz * 12
end if
end function

Standard Deviation for SQLite

I've searched the SQLite docs and couldn't find anything, but I've also searched on Google and a few results appeared.
Does SQLite have any built-in Standard Deviation function?
You can calculate the variance in SQL:
create table t (row int);
insert into t values (1),(2),(3);
SELECT AVG((t.row - sub.a) * (t.row - sub.a)) as var from t,
(SELECT AVG(row) AS a FROM t) AS sub;
0.666666666666667
However, you still have to calculate the square root to get the standard deviation.
The aggregate functions supported by SQLite are here:
http://www.sqlite.org/lang_aggfunc.html
STDEV is not in the list.
However, the module extension-functions.c in this page contains a STDEV function.
There is still no built-in stdev function in sqlite. However, you can define (as Alix has done) a user-defined aggregator function. Here is a complete example in Python:
import sqlite3
import math
class StdevFunc:
def __init__(self):
self.M = 0.0
self.S = 0.0
self.k = 1
def step(self, value):
if value is None:
return
tM = self.M
self.M += (value - tM) / self.k
self.S += (value - tM) * (value - self.M)
self.k += 1
def finalize(self):
if self.k < 3:
return None
return math.sqrt(self.S / (self.k-2))
with sqlite3.connect(':memory:') as con:
con.create_aggregate("stdev", 1, StdevFunc)
cur = con.cursor()
cur.execute("create table test(i)")
cur.executemany("insert into test(i) values (?)", [(1,), (2,), (3,), (4,), (5,)])
cur.execute("insert into test(i) values (null)")
cur.execute("select avg(i) from test")
print("avg: %f" % cur.fetchone()[0])
cur.execute("select stdev(i) from test")
print("stdev: %f" % cur.fetchone()[0])
This will print:
avg: 3.000000
stdev: 1.581139
Compare with MySQL: http://sqlfiddle.com/#!2/ad42f3/3/0
Use variance formula V(X) = E(X^2) - E(X)^2. In SQL sqlite
SELECT AVG(col*col) - AVG(col)*AVG(col) FROM table
To get standard deviation you need to take the square root V(X)^(1/2)
I implemented the Welford's method (the same as extension-functions.c) as a SQLite UDF:
$db->sqliteCreateAggregate('stdev',
function (&$context, $row, $data) // step callback
{
if (isset($context) !== true) // $context is null at first
{
$context = array
(
'k' => 0,
'm' => 0,
's' => 0,
);
}
if (isset($data) === true) // the standard is non-NULL values only
{
$context['s'] += ($data - $context['m']) * ($data - ($context['m'] += ($data - $context['m']) / ++$context['k']));
}
return $context;
},
function (&$context, $row) // fini callback
{
if ($context['k'] > 0) // return NULL if no non-NULL values exist
{
return sqrt($context['s'] / $context['k']);
}
return null;
},
1);
That's in PHP ($db is the PDO object) but it should be trivial to port to another language.
SQLite is soooo cool. <3
a little trick
select ((sum(value)*sum(value) - sum(value * value))/((count(*)-1)*(count(*))))
from the_table ;
then the only thing left is to calculate sqrt outside.
No, I searched this same issue, and ended having to do the calculations with my application (PHP)
added some error detection in the python functions
class StdevFunc:
"""
For use as an aggregate function in SQLite
"""
def __init__(self):
self.M = 0.0
self.S = 0.0
self.k = 0
def step(self, value):
try:
# automatically convert text to float, like the rest of SQLite
val = float(value) # if fails, skips this iteration, which also ignores nulls
tM = self.M
self.k += 1
self.M += ((val - tM) / self.k)
self.S += ((val - tM) * (val - self.M))
except:
pass
def finalize(self):
if self.k <= 1: # avoid division by zero
return none
else:
return math.sqrt(self.S / (self.k-1))
You don't state which version of standard deviation you wish to calculate but variances (standard deviation squared) for either version can be calculated using a combination of the sum() and count() aggregate functions.
select
(count(val)*sum(val*val) - (sum(val)*sum(val)))/((count(val)-1)*(count(val))) as sample_variance,
(count(val)*sum(val*val) - (sum(val)*sum(val)))/((count(val))*(count(val))) as population_variance
from ... ;
It will still be necessary to take the square root of these to obtain the standard deviation.
#!/usr/bin/python
# -*- coding: utf-8 -*-
#Values produced by this script can be verified by follwing the steps
#found at https://support.microsoft.com/en-us/kb/213930 to Verify
#by chosing a non memory based database.
import sqlite3
import math
import random
import os
import sys
import traceback
import random
class StdevFunc:
def __init__(self):
self.M = 0.0 #Mean
self.V = 0.0 #Used to Calculate Variance
self.S = 0.0 #Standard Deviation
self.k = 1 #Population or Small
def step(self, value):
try:
if value is None:
return None
tM = self.M
self.M += (value - tM) / self.k
self.V += (value - tM) * (value - self.M)
self.k += 1
except Exception as EXStep:
pass
return None
def finalize(self):
try:
if ((self.k - 1) < 3):
return None
#Now with our range Calculated, and Multiplied finish the Variance Calculation
self.V = (self.V / (self.k-2))
#Standard Deviation is the Square Root of Variance
self.S = math.sqrt(self.V)
return self.S
except Exception as EXFinal:
pass
return None
def Histogram(Population):
try:
BinCount = 6
More = 0
#a = 1 #For testing Trapping
#b = 0 #and Trace Back
#c = (a / b) #with Detailed Info
#If you want to store the Database
#uncDatabase = os.path.join(os.getcwd(),"BellCurve.db3")
#con = sqlite3.connect(uncDatabase)
#If you want the database in Memory
con = sqlite3.connect(':memory:')
#row_factory allows accessing fields by Row and Col Name
con.row_factory = sqlite3.Row
#Add our Non Persistent, Runtime Standard Deviation Function to the Database
con.create_aggregate("Stdev", 1, StdevFunc)
#Lets Grab a Cursor
cur = con.cursor()
#Lets Initialize some tables, so each run with be clear of previous run
cur.executescript('drop table if exists MyData;') #executescript requires ; at the end of the string
cur.execute("create table IF NOT EXISTS MyData('ID' INTEGER PRIMARY KEY AUTOINCREMENT, 'Val' FLOAT)")
cur.executescript('drop table if exists Bins;') #executescript requires ; at the end of the string
cur.execute("create table IF NOT EXISTS Bins('ID' INTEGER PRIMARY KEY AUTOINCREMENT, 'Bin' UNSIGNED INTEGER, 'Val' FLOAT, 'Frequency' UNSIGNED BIG INT)")
#Lets generate some random data, and insert in to the Database
for n in range(0,(Population)):
sql = "insert into MyData(Val) values ({0})".format(random.uniform(-1,1))
#If Whole Number Integer greater that value of 2, Range Greater that 1.5
#sql = "insert into MyData(Val) values ({0})".format(random.randint(-1,1))
cur.execute(sql)
pass
#Now let’s calculate some built in Aggregates, that SQLite comes with
cur.execute("select Avg(Val) from MyData")
Average = cur.fetchone()[0]
cur.execute("select Max(Val) from MyData")
Max = cur.fetchone()[0]
cur.execute("select Min(Val) from MyData")
Min = cur.fetchone()[0]
cur.execute("select Count(Val) from MyData")
Records = cur.fetchone()[0]
#Now let’s get Standard Deviation using our function that we added
cur.execute("select Stdev(Val) from MyData")
Stdev = cur.fetchone()[0]
#And Calculate Range
Range = float(abs(float(Max)-float(Min)))
if (Stdev == None):
print("================================ Data Error ===============================")
print(" Insufficient Population Size, Or Bad Data.")
print("*****************************************************************************")
elif (abs(Max-Min) == 0):
print("================================ Data Error ===============================")
print(" The entire Population Contains Identical values, Distribution Incalculable.")
print("******************************************************************************")
else:
Bin = [] #Holds the Bin Values
Frequency = [] #Holds the Bin Frequency for each Bin
#Establish the 1st Bin, which is based on (Standard Deviation * 3) being subtracted from the Mean
Bin.append(float((Average - ((3 * Stdev)))))
Frequency.append(0)
#Establish the remaining Bins, which is basically adding 1 Standard Deviation
#for each interation, -3, -2, -1, 1, 2, 3
for b in range(0,(BinCount) + 1):
Bin.append((float(Bin[(b)]) + Stdev))
Frequency.append(0)
for b in range(0,(BinCount) + 1):
#Lets exploit the Database and have it do the hard work calculating distribution
#of all the Bins, with SQL's between operator, but making it left inclusive, right exclusive.
sqlBinFreq = "select count(*) as Frequency from MyData where val between {0} and {1} and Val < {2}". \
format(float((Bin[b])), float(Bin[(b + 1)]), float(Bin[(b + 1)]))
#If the Database Reports Values that fall between the Current Bin, Store the Frequency to a Bins Table.
for rowBinFreq in cur.execute(sqlBinFreq):
Frequency[(b + 1)] = rowBinFreq['Frequency']
sqlBinFreqInsert = "insert into Bins (Bin, Val, Frequency) values ({0}, {1}, {2})". \
format(b, float(Bin[b]), Frequency[(b)])
cur.execute(sqlBinFreqInsert)
#Allthough this Demo is not likley produce values that
#fall outside of Standard Distribution
#if this demo was to Calculate with real data, we want to know
#how many non-Standard data points we have.
More = (More + Frequency[b])
More = abs((Records - More))
#Add the More value
sqlBinFreqInsert = "insert into Bins (Bin, Val, Frequency) values ({0}, {1}, {2})". \
format((BinCount + 1), float(0), More)
cur.execute(sqlBinFreqInsert)
#Now Report the Analysis
print("================================ The Population ==============================")
print(" {0} {1} {2} {3} {4} {5}". \
format("Size".rjust(10, ' '), \
"Max".rjust(10, ' '), \
"Min".rjust(10, ' '), \
"Mean".rjust(10, ' '), \
"Range".rjust(10, ' '), \
"Stdev".rjust(10, ' ')))
print("Aggregates: {0:10d} {1:10.4f} {2:10.4f} {3:10.4f} {4:10.4f} {5:10.4f}". \
format(Population, Max, Min, Average, Range, Stdev))
print("================================= The Bell Curve =============================")
LabelString = "{0} {1} {2} {3}". \
format("Bin".ljust(8, ' '), \
"Ranges".rjust(8, ' '), \
"Frequency".rjust(8, ' '), \
"Histogram".rjust(6, ' '))
print(LabelString)
print("------------------------------------------------------------------------------")
#Let's Paint a Histogram
sqlChart = "select * from Bins order by Bin asc"
for rowChart in cur.execute(sqlChart):
if (rowChart['Bin'] == 7):
#Bin 7 is not really a bin, but where we place the values that did not fit into the
#Normal Distribution. This script was tested against Excel's Bell Curve Example
#https://support.microsoft.com/en-us/kb/213930
#and produces the same results. Feel free to test it.
BinName = "More"
ChartString = "{0:<6} {1:<10} {2:10.0f}". \
format(BinName, \
"", \
More)
else:
#Theses are the actual bins where values fall within the distribution.
BinName = (rowChart['Bin'] + 1)
#Scale the Chart
fPercent = ((float(rowChart['Frequency']) / float(Records) * 100))
iPrecent = int(math.ceil(fPercent))
ChartString = "{0:<6} {1:10.4f} {2:10.0f} {3}". \
format(BinName, \
rowChart['Val'], \
rowChart['Frequency'], \
"".rjust(iPrecent, '#'))
print(ChartString)
print("******************************************************************************")
#Commit to Database
con.commit()
#Clean Up
cur.close()
con.close()
except Exception as EXBellCurve:
pass
TraceInfo = traceback.format_exc()
raise Exception(TraceInfo)

Evaluating a string of simple mathematical expressions [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Closed 10 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
Challenge
Here is the challenge (of my own invention, though I wouldn't be surprised if it has previously appeared elsewhere on the web).
Write a function that takes a single
argument that is a
string representation of a simple
mathematical expression and evaluates
it as a floating point value. A
"simple expression" may include any of
the following: positive or negative
decimal numbers, +, -, *, /, (, ).
Expressions use (normal) infix notation.
Operators should be evaluated in the
order they appear, i.e. not as in
BODMAS,
though brackets should be correctly
observed, of course. The function should return
the correct result for any possible expression
of this form. However, the function does not have
to handle malformed expressions (i.e. ones with bad syntax).
Examples of expressions:
1 + 3 / -8 = -0.5 (No BODMAS)
2*3*4*5+99 = 219
4 * (9 - 4) / (2 * 6 - 2) + 8 = 10
1 + ((123 * 3 - 69) / 100) = 4
2.45/8.5*9.27+(5*0.0023) = 2.68...
Rules
I anticipate some form of "cheating"/craftiness here, so please let me forewarn against it! By cheating, I refer to the use of the eval or equivalent function in dynamic languages such as JavaScript or PHP, or equally compiling and executing code on the fly. (I think my specification of "no BODMAS" has pretty much guaranteed this however.) Apart from that, there are no restrictions. I anticipate a few Regex solutions here, but it would be nice to see more than just that.
Now, I'm mainly interested in a C#/.NET solution here, but any other language would be perfectly acceptable too (in particular, F# and Python for the functional/mixed approaches). I haven't yet decided whether I'm going to accept the shortest or most ingenious solution (at least for the language) as the answer, but I would welcome any form of solution in any language, except what I've just prohibited above!
My Solution
I've now posted my C# solution here (403 chars). Update: My new solution has beaten the old one significantly at 294 chars, with the help of a bit of lovely regex! I suspected that this will get easily beaten by some of the languages out there with lighter syntax (particularly the funcional/dynamic ones), and have been proved right, but I'd be curious if someone could beat this in C# still.
Update
I've seen some very crafty solutions already. Thanks to everyone who has posted one. Although I haven't tested any of them yet, I'm going to trust people and assume they at least work with all of the given examples.
Just for the note, re-entrancy (i.e. thread-safety) is not a requirement for the function, though it is a bonus.
Format
Please post all answers in the following format for the purpose of easy comparison:
Language
Number of characters: ???
Fully obfuscated function:
(code here)
Clear/semi-obfuscated function:
(code here)
Any notes on the algorithm/clever shortcuts it takes.
Assembler
427 bytes
Obfuscated, assembled with the excellent A86 into a .com executable:
dd 0db9b1f89h, 081bee3h, 0e8af789h, 0d9080080h, 0bdac7674h, 013b40286h
dd 07400463ah, 0ccfe4508h, 08ce9f675h, 02fc8000h, 013b0057eh, 0feaac42ah
dd 0bedf75c9h, 0ba680081h, 04de801h, 04874f73bh, 04474103ch, 0e8e8b60fh
dd 08e8a003fh, 0e880290h, 0de0153h, 08b57e6ebh, 0d902a93eh, 046d891dh
dd 08906c783h, 05f02a93eh, 03cffcee8h, 057197510h, 02a93e8bh, 08b06ef83h
dd 05d9046dh, 02a93e89h, 03bc9d95fh, 0ac0174f7h, 074f73bc3h, 0f3cac24h
dd 0eed9c474h, 0197f0b3ch, 07cc4940fh, 074f73b09h, 0103cac09h, 0a3ce274h
dd 0e40a537eh, 0e0d90274h, 02a3bac3h, 021cd09b4h, 03e8b20cdh, 0ff8102a9h
dd 0ed7502abh, 0474103ch, 0e57d0b3ch, 0be02a3bfh, 014d903a3h, 0800344f6h
dd 02db00574h, 0d9e0d9aah, 0d9029f2eh, 0bb34dfc0h, 08a0009h, 01c75f0a8h
dd 020750fa8h, 0b0f3794bh, 021e9aa30h, 0de607400h, 08802990eh, 0de07df07h
dd 0c392ebc1h, 0e8c0008ah, 0aa300404h, 0f24008ah, 04baa3004h, 02eb0ee79h
dd 03005c6aah, 0c0d90ab1h, 0e9defcd9h, 02a116deh, 0e480e0dfh, 040fc8045h
dd 0ede1274h, 0c0d90299h, 015dffcd9h, 047300580h, 0de75c9feh, 0303d804fh
dd 03d80fa74h, 04f01752eh, 0240145c6h, 0dfff52e9h, 0d9029906h, 0f73b025fh
dd 03caca174h, 07fed740ah, 0df07889ah, 0277d807h, 047d9c1deh, 0990ede02h
dd 025fd902h, 03130e0ebh, 035343332h, 039383736h, 02f2b2d2eh, 02029282ah
dd 0e9000a09h, 07fc9f9c1h, 04500000fh, 0726f7272h
db 024h, 0abh, 02h
EDIT: Unobfuscated source:
mov [bx],bx
finit
mov si,81h
mov di,si
mov cl,[80h]
or cl,bl
jz ret
l1:
lodsb
mov bp,d1
mov ah,19
l2:
cmp al,[bp]
je l3
inc bp
dec ah
jne l2
jmp exit
l3:
cmp ah,2
jle l4
mov al,19
sub al,ah
stosb
l4:
dec cl
jnz l1
mov si,81h
push done
decode:
l5:
call l7
l50:
cmp si,di
je ret
cmp al,16
je ret
db 0fh, 0b6h, 0e8h ; movzx bp,al
call l7
mov cl,[bp+op-11]
mov byte ptr [sm1],cl
db 0deh
sm1:db ?
jmp l50
open:
push di
mov di,word ptr [s]
fstp dword ptr [di]
mov [di+4],bp
add di,6
mov word ptr [s],di
pop di
call decode
cmp al,16
jne ret
push di
mov di,word ptr [s]
sub di,6
mov bp,[di+4]
fld dword ptr [di]
mov word ptr [s],di
pop di
fxch st(1)
cmp si,di
je ret
lodsb
ret
l7: cmp si,di
je exit
lodsb
cmp al,15
je open
fldz
cmp al,11
jg exit
db 0fh, 94h, 0c4h ; sete ah
jl l10
l9:
cmp si,di
je l12
lodsb
cmp al,16
je ret
l10:
cmp al,10
jle l12i
l12:
or ah,ah
je l13
fchs
l13:
ret
exit:
mov dx,offset res
mov ah,9
int 21h
int 20h
done:
mov di,word ptr [s]
cmp di,(offset s)+2
jne exit
cmp al,16
je ok
cmp al,11
jge exit
ok:
mov di,res
mov si,res+100h
fst dword ptr [si]
test byte ptr [si+3],80h
jz pos
mov al,'-'
stosb
fchs
pos:
fldcw word ptr [cw]
fld st(0)
fbstp [si]
mov bx,9
l1000:
mov al,[si+bx]
test al,0f0h
jne startu
test al,0fh
jne startl
dec bx
jns l1000
mov al,'0'
stosb
jmp frac
l12i:
je l11
fimul word ptr [d3]
mov [bx],al
fild word ptr [bx]
faddp
jmp l9
ret
startu:
mov al,[si+bx]
shr al,4
add al,'0'
stosb
startl:
mov al,[si+bx]
and al,0fh
add al,'0'
stosb
dec bx
jns startu
frac:
mov al,'.'
stosb
mov byte ptr [di],'0'
mov cl,10
fld st(0)
frndint
frac1:
fsubp st(1)
ficom word ptr [zero]
fstsw ax
and ah,045h
cmp ah,040h
je finished
fimul word ptr [d3]
fld st(0)
frndint
fist word ptr [di]
add byte ptr [di],'0'
inc di
dec cl
jnz frac1
finished:
dec di
cmp byte ptr [di],'0'
je finished
cmp byte ptr [di],'.'
jne f2
dec di
f2:
mov byte ptr [di+1],'$'
exit2:
jmp exit
l11:
fild word ptr [d3]
fstp dword ptr [bx+2]
l111:
cmp si,di
je ret
lodsb
cmp al,10
je exit2
jg ret
mov [bx],al
fild word ptr [bx]
fdiv dword ptr [bx+2]
faddp
fld dword ptr [bx+2]
fimul word ptr [d3]
fstp dword ptr [bx+2]
jmp l111
d1: db '0123456789.-+/*()', 32, 9
d3: dw 10
op: db 0e9h, 0c1h, 0f9h, 0c9h
cw: dw 0f7fh
zero: dw 0
res:db 'Error$'
s: dw (offset s)+2
Perl (no eval)
Number of characters: 167 106 (see below for the 106 character version)
Fully obfuscated function: (167 characters if you join these three lines into one)
sub e{my$_="($_[0])";s/\s//g;$n=q"(-?\d++(\.\d+)?+)";
#a=(sub{$1},1,sub{$3*$6},sub{$3+$6},4,sub{$3-$6},6,sub{$3/$6});
while(s:\($n\)|(?<=\()$n(.)$n:$a[7&ord$5]():e){}$_}
Clear/deobfuscated version:
sub e {
my $_ = "($_[0])";
s/\s//g;
$n=q"(-?\d++(\.\d+)?+)"; # a regex for "number", including capturing groups
# q"foo" in perl means the same as 'foo'
# Note the use of ++ and ?+ to tell perl
# "no backtracking"
#a=(sub{$1}, # 0 - no operator found
1, # placeholder
sub{$3*$6}, # 2 - ord('*') = 052
sub{$3+$6}, # 3 - ord('+') = 053
4, # placeholder
sub{$3-$6}, # 5 - ord('-') = 055
6, # placeholder
sub{$3/$6}); # 7 - ord('/') = 057
# The (?<=... bit means "find a NUM WHATEVER NUM sequence that happens
# immediately after a left paren", without including the left
# paren. The while loop repeatedly replaces "(" NUM WHATEVER NUM with
# "(" RESULT and "(" NUM ")" with NUM. The while loop keeps going
# so long as those replacements can be made.
while(s:\($n\)|(?<=\()$n(.)$n:$a[7&ord$5]():e){}
# A perl function returns the value of the last statement
$_
}
I had misread the rules initially, so I'd submitted a version with "eval". Here's a version without it.
The latest bit of insight came when I realized that the last octal digit in the character codes for +, -, /, and * is different, and that ord(undef) is 0. This lets me set up the dispatch table #a as an array, and just invoke the code at the location 7 & ord($3).
There's an obvious spot to shave off one more character - change q"" into '' - but that would make it harder to cut-and-paste into the shell.
Even shorter
Number of characters: 124 106
Taking edits by ephemient into account, it's now down to 124 characters: (join the two lines into one)
sub e{$_=$_[0];s/\s//g;$n=q"(-?\d++(\.\d+)?+)";
1while s:\($n\)|$n(.)$n:($1,1,$3*$6,$3+$6,4,$3-$6,6,$6&&$3/$6)[7&ord$5]:e;$_}
Shorter still
Number of characters: 110 106
The ruby solution down below is pushing me further, though I can't reach its 104 characters:
sub e{($_)=#_;$n='( *-?[.\d]++ *)';
s:\($n\)|$n(.)$n:(($1,$2-$4,$4&&$2/$4,$2*$4,$2+$4)x9)[.8*ord$3]:e?e($_):$_}
I had to give in and use ''. That ruby send trick is really useful for this problem.
Squeezing water from a stone
Number of characters: 106
A small contortion to avoid the divide-by-zero check.
sub e{($_)=#_;$n='( *-?[.\d]++ *)';
s:\($n\)|$n(.)$n:($1,0,$2*$4,$2+$4,0,$2-$4)[7&ord$3]//$2/$4:e?e($_):$_}
Here's the test harness for this function:
perl -le 'sub e{($_)=#_;$n='\''( *-?[.\d]++ *)'\'';s:\($n\)|$n(.)$n:($1,0,$2*$4,$2+$4,0,$2-$4)[7&ord$3]//$2/$4:e?e($_):$_}' -e 'print e($_) for #ARGV' '1 + 3' '1 + ((123 * 3 - 69) / 100)' '4 * (9 - 4) / (2 * 6 - 2) + 8' '2*3*4*5+99' '2.45/8.5*9.27+(5*0.0023) ' '1 + 3 / -8'
Ruby
Number of characters: 103
N='( *-?[\d.]+ *)'
def e x
x.sub!(/\(#{N}\)|#{N}([^.\d])#{N}/){$1or(e$2).send$3,e($4)}?e(x):x.to_f
end
This is a non-recursive version of The Wicked Flea's solution. Parenthesized sub-expressions are evaluated bottom-up instead of top-down.
Edit: Converting the 'while' to a conditional + tail recursion has saved a few characters, so it is no longer non-recursive (though the recursion is not semantically necessary.)
Edit: Borrowing Daniel Martin's idea of merging the regexps saves another 11 characters!
Edit: That recursion is even more useful than I first thought! x.to_f can be rewritten as e(x), if x happens to contain a single number.
Edit: Using 'or' instead of '||' allows a pair of parentheses to be dropped.
Long version:
# Decimal number, as a capturing group, for substitution
# in the main regexp below.
N='( *-?[\d.]+ *)'
# The evaluation function
def e(x)
matched = x.sub!(/\(#{N}\)|#{N}([^\d.])#{N}/) do
# Group 1 is a numeric literal in parentheses. If this is present then
# just return it.
if $1
$1
# Otherwise, $3 is an operator symbol and $2 and $4 are the operands
else
# Recursively call e to parse the operands (we already know from the
# regexp that they are numeric literals, and this is slightly shorter
# than using :to_f)
e($2).send($3, e($4))
# We could have converted $3 to a symbol ($3.to_s) or converted the
# result back to string form, but both are done automatically anyway
end
end
if matched then
# We did one reduction. Now recurse back and look for more.
e(x)
else
# If the string doesn't look like a non-trivial expression, assume it is a
# string representation of a real number and attempt to parse it
x.to_f
end
end
C (VS2005)
Number of Characters: 1360
Abuse of preprocessor and warnings for fun code layout (scroll down to see):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define b main
#define c(a) b(a,0)
#define d -1
#define e -2
#define g break
#define h case
#define hh h
#define hhh h
#define w(i) case i
#define i return
#define j switch
#define k float
#define l realloc
#define m sscanf
#define n int _
#define o char
#define t(u) #u
#define q(r) "%f" t(r) "n"
#define s while
#define v default
#define ex exit
#define W printf
#define x fn()
#define y strcat
#define z strcpy
#define Z strlen
char*p =0 ;k *b (n,o** a){k*f
;j(_){ hh e: i* p==40? (++p,c
(d )) :( f= l( 0,
4) ,m (p ,q (% ),
f,&_), p+=_ ,f ); hh
d:f=c( e);s (1 ){ j(
*p ++ ){ hh 0: hh
41 :i f; hh 43 :*
f+=*c( e) ;g ;h 45:*f= *f-*c(
e);g;h 42 :* f= *f**c( e);g;h
47:*f /=*c (e); g; v: c(0);}
}w(1): if(p&& printf (q (( "\\"))
,* c( d) )) g; hh 0: ex (W
(x )) ;v :p =( p?y: z)(l(p
,Z(1[ a] )+ (p ?Z(p )+
1:1)) ,1 [a ]) ;b (_ -1 ,a
+1 ); g; }i 0;};fn () {n =42,p=
43 ;i "Er" "ro" t( r) "\n";}
Visual Basic.NET
Number of characters: 9759
I'm more of a bowler myself.
NOTE: does not take nested parentheses into account. Also, untested, but I'm pretty sure it works.
Imports Microsoft.VisualBasic
Imports System.Text
Imports System.Collections.Generic
Public Class Main
Public Shared Function DoArithmaticFunctionFromStringInput(ByVal MathematicalString As String) As Double
Dim numberList As New List(Of Number)
Dim operationsList As New List(Of IOperatable)
Dim currentNumber As New Number
Dim currentParentheticalStatement As New Parenthetical
Dim isInParentheticalMode As Boolean = False
Dim allCharactersInString() As Char = MathematicalString.ToCharArray
For Each mathChar In allCharactersInString
If mathChar = Number.ZERO_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.ONE_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.TWO_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.THREE_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.FOUR_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.FIVE_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.SIX_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.SEVEN_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.EIGHT_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.NINE_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Number.DECIMAL_POINT_STRING_REPRESENTATION Then
currentNumber.UpdateNumber(mathChar)
ElseIf mathChar = Addition.ADDITION_STRING_REPRESENTATION Then
Dim addition As New Addition
If Not isInParentheticalMode Then
operationsList.Add(addition)
numberList.Add(currentNumber)
Else
currentParentheticalStatement.AllNumbers.Add(currentNumber)
currentParentheticalStatement.AllOperators.Add(addition)
End If
currentNumber = New Number
ElseIf mathChar = Number.NEGATIVE_NUMBER_STRING_REPRESENTATION Then
If currentNumber.StringOfNumbers.Length > 0 Then
currentNumber.UpdateNumber(mathChar)
Dim subtraction As New Addition
If Not isInParentheticalMode Then
operationsList.Add(subtraction)
numberList.Add(currentNumber)
Else
currentParentheticalStatement.AllNumbers.Add(currentNumber)
currentParentheticalStatement.AllOperators.Add(subtraction)
End If
currentNumber = New Number
Else
currentNumber.UpdateNumber(mathChar)
End If
ElseIf mathChar = Multiplication.MULTIPLICATION_STRING_REPRESENTATION Then
Dim multiplication As New Multiplication
If Not isInParentheticalMode Then
operationsList.Add(multiplication)
numberList.Add(currentNumber)
Else
currentParentheticalStatement.AllNumbers.Add(currentNumber)
currentParentheticalStatement.AllOperators.Add(multiplication)
End If
currentNumber = New Number
ElseIf mathChar = Division.DIVISION_STRING_REPRESENTATION Then
Dim division As New Division
If Not isInParentheticalMode Then
operationsList.Add(division)
numberList.Add(currentNumber)
Else
currentParentheticalStatement.AllNumbers.Add(currentNumber)
currentParentheticalStatement.AllOperators.Add(division)
End If
currentNumber = New Number
ElseIf mathChar = Parenthetical.LEFT_PARENTHESIS_STRING_REPRESENTATION Then
isInParentheticalMode = True
ElseIf mathChar = Parenthetical.RIGHT_PARENTHESIS_STRING_REPRESENTATION Then
currentNumber = currentParentheticalStatement.EvaluateParentheticalStatement
numberList.Add(currentNumber)
isInParentheticalMode = False
End If
Next
Dim result As Double = 0
Dim operationIndex As Integer = 0
For Each numberOnWhichToPerformOperations As Number In numberList
result = operationsList(operationIndex).PerformOperation(result, numberOnWhichToPerformOperations)
operationIndex = operationIndex + 1
Next
Return result
End Function
Public Class Number
Public Const DECIMAL_POINT_STRING_REPRESENTATION As Char = "."
Public Const NEGATIVE_NUMBER_STRING_REPRESENTATION As Char = "-"
Public Const ZERO_STRING_REPRESENTATION As Char = "0"
Public Const ONE_STRING_REPRESENTATION As Char = "1"
Public Const TWO_STRING_REPRESENTATION As Char = "2"
Public Const THREE_STRING_REPRESENTATION As Char = "3"
Public Const FOUR_STRING_REPRESENTATION As Char = "4"
Public Const FIVE_STRING_REPRESENTATION As Char = "5"
Public Const SIX_STRING_REPRESENTATION As Char = "6"
Public Const SEVEN_STRING_REPRESENTATION As Char = "7"
Public Const EIGHT_STRING_REPRESENTATION As Char = "8"
Public Const NINE_STRING_REPRESENTATION As Char = "9"
Private _isNegative As Boolean
Public ReadOnly Property IsNegative() As Boolean
Get
Return _isNegative
End Get
End Property
Public ReadOnly Property ActualNumber() As Double
Get
Dim result As String = ""
If HasDecimal Then
If DecimalIndex = StringOfNumbers.Length - 1 Then
result = StringOfNumbers.ToString
Else
result = StringOfNumbers.Insert(DecimalIndex, DECIMAL_POINT_STRING_REPRESENTATION).ToString
End If
Else
result = StringOfNumbers.ToString
End If
If IsNegative Then
result = NEGATIVE_NUMBER_STRING_REPRESENTATION & result
End If
Return CType(result, Double)
End Get
End Property
Private _hasDecimal As Boolean
Public ReadOnly Property HasDecimal() As Boolean
Get
Return _hasDecimal
End Get
End Property
Private _decimalIndex As Integer
Public ReadOnly Property DecimalIndex() As Integer
Get
Return _decimalIndex
End Get
End Property
Private _stringOfNumbers As New StringBuilder
Public ReadOnly Property StringOfNumbers() As StringBuilder
Get
Return _stringOfNumbers
End Get
End Property
Public Sub UpdateNumber(ByVal theDigitToAppend As Char)
If IsNumeric(theDigitToAppend) Then
Me._stringOfNumbers.Append(theDigitToAppend)
ElseIf theDigitToAppend = DECIMAL_POINT_STRING_REPRESENTATION Then
Me._hasDecimal = True
Me._decimalIndex = Me._stringOfNumbers.Length
ElseIf theDigitToAppend = NEGATIVE_NUMBER_STRING_REPRESENTATION Then
Me._isNegative = Not Me._isNegative
End If
End Sub
Public Shared Function ConvertDoubleToNumber(ByVal numberThatIsADouble As Double) As Number
Dim numberResult As New Number
For Each character As Char In numberThatIsADouble.ToString.ToCharArray
numberResult.UpdateNumber(character)
Next
Return numberResult
End Function
End Class
Public MustInherit Class Operation
Protected _firstnumber As New Number
Protected _secondnumber As New Number
Public Property FirstNumber() As Number
Get
Return _firstnumber
End Get
Set(ByVal value As Number)
_firstnumber = value
End Set
End Property
Public Property SecondNumber() As Number
Get
Return _secondnumber
End Get
Set(ByVal value As Number)
_secondnumber = value
End Set
End Property
End Class
Public Interface IOperatable
Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double
End Interface
Public Class Addition
Inherits Operation
Implements IOperatable
Public Const ADDITION_STRING_REPRESENTATION As String = "+"
Public Sub New()
End Sub
Public Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double Implements IOperatable.PerformOperation
Dim result As Double = 0
result = number1 + number2.ActualNumber
Return result
End Function
End Class
Public Class Multiplication
Inherits Operation
Implements IOperatable
Public Const MULTIPLICATION_STRING_REPRESENTATION As String = "*"
Public Sub New()
End Sub
Public Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double Implements IOperatable.PerformOperation
Dim result As Double = 0
result = number1 * number2.ActualNumber
Return result
End Function
End Class
Public Class Division
Inherits Operation
Implements IOperatable
Public Const DIVISION_STRING_REPRESENTATION As String = "/"
Public Const DIVIDE_BY_ZERO_ERROR_MESSAGE As String = "I took a lot of time to write this program. Please don't be a child and try to defile it by dividing by zero. Nobody thinks you are funny."
Public Sub New()
End Sub
Public Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double Implements IOperatable.PerformOperation
If Not number2.ActualNumber = 0 Then
Dim result As Double = 0
result = number1 / number2.ActualNumber
Return result
Else
Dim divideByZeroException As New Exception(DIVIDE_BY_ZERO_ERROR_MESSAGE)
Throw divideByZeroException
End If
End Function
End Class
Public Class Parenthetical
Public Const LEFT_PARENTHESIS_STRING_REPRESENTATION As String = "("
Public Const RIGHT_PARENTHESIS_STRING_REPRESENTATION As String = ")"
Private _allNumbers As New List(Of Number)
Public Property AllNumbers() As List(Of Number)
Get
Return _allNumbers
End Get
Set(ByVal value As List(Of Number))
_allNumbers = value
End Set
End Property
Private _allOperators As New List(Of IOperatable)
Public Property AllOperators() As List(Of IOperatable)
Get
Return _allOperators
End Get
Set(ByVal value As List(Of IOperatable))
_allOperators = value
End Set
End Property
Public Sub New()
End Sub
Public Function EvaluateParentheticalStatement() As Number
Dim result As Double = 0
Dim operationIndex As Integer = 0
For Each numberOnWhichToPerformOperations As Number In AllNumbers
result = AllOperators(operationIndex).PerformOperation(result, numberOnWhichToPerformOperations)
operationIndex = operationIndex + 1
Next
Dim numberToReturn As New Number
numberToReturn = Number.ConvertDoubleToNumber(result)
Return numberToReturn
End Function
End Class
End Class
Haskell
Number of characters: 182
No attempt at cleverness, just some compression: 4 lines, 312 bytes.
import Data.Char;import Text.ParserCombinators.Parsec
q=either(error.show)id.runParser t id"".filter(' '/=);t=do
s<-getState;a<-fmap read(many1$oneOf".-"<|>digit)<|>between(char '('>>setState id)(char ')'>>setState s)t
option(s a)$choice(zipWith(\c o->char c>>return(o$s a))"+-*/"[(+),(-),(*),(/)])>>=setState>>t
And now, really getting into the golf spirit, 3 lines and 182 bytes:
q=snd.(`e`id).filter(' '/=)
e s c|[(f,h)]<-readsPrec 0 s=g h(c f);e('(':s)c=g h(c f)where(')':h,f)=e s id
g('+':h)=e h.(+);g('-':h)=e h.(-);g('*':h)=e h.(*);g('/':h)=e h.(/);g h=(,)h
Exploded:
-- Strip spaces from the input, evaluate with empty accumulator,
-- and output the second field of the result.
q :: String -> Double
q = snd . flip eval id . filter (not . isSpace)
-- eval takes a string and an accumulator, and returns
-- the final value and what’s left unused from the string.
eval :: (Fractional a, Read a) => String -> (a -> a) -> (String, a)
-- If the beginning of the string parses as a number, add it to the accumulator,
-- then try to read an operator and further.
eval str accum | [(num, rest)] <- readsPrec 0 str = oper rest (accum num)
-- If the string starts parentheses, evaluate the inside with a fresh
-- accumulator, and continue after the closing paren.
eval ('(':str) accum = oper rest (accum num) where (')':rest, num) = eval str id
-- oper takes a string and current value, and tries to read an operator
-- to apply to the value. If there is none, it’s okay.
oper :: (Fractional a, Read a) => String -> a -> (String, a)
-- Handle operations by giving eval a pre-seeded accumulator.
oper ('+':str) num = eval str (num +)
oper ('-':str) num = eval str (num -)
oper ('*':str) num = eval str (num *)
oper ('/':str) num = eval str (num /)
-- If there’s no operation parsable, just return.
oper str num = (str, num)
Python
Number of characters: 237
Fully obfuscated function:
from operator import*
def e(s,l=[]):
if s:l+=list(s.replace(' ','')+')')
a=0;o=add;d=dict(zip(')*+-/',(0,mul,o,sub,div)));p=l.pop
while o:
c=p(0)
if c=='(':c=e(0)
while l[0]not in d:c+=p(0)
a=o(a,float(c));o=d[p(0)]
return a
Clear/semi-obfuscated function:
import operator
def calc(source, stack=[]):
if source:
stack += list(source.replace(' ', '') + ')')
answer = 0
ops = {
')': 0,
'*': operator.mul,
'+': operator.add,
'-': operator.sub,
'/': operator.div,
}
op = operator.add
while op:
cur = stack.pop(0)
if cur == '(':
cur = calc(0)
while stack[0] not in ops:
cur += stack.pop(0)
answer = op(answer, float(cur))
op = ops[stack.pop(0)]
return answer
Fortran 77 (gfortran dialect, now with g77 support)
Number of characters: 2059
Obfuscated version:
function e(c)
character*99 c
character b
real f(24)
integer i(24)
nf=0
ni=0
20 nf=kf(0.0,nf,f)
ni=ki(43,ni,i)
30 if (isp(c).eq.1) goto 20
h=fr(c)
31 g=fp(nf,f)
j=ip(ni,i)
select case(j)
case (40)
goto 20
case (42)
d=g*h
case (43)
d=g+h
case (45)
d=g-h
case (47)
d=g/h
end select
50 nf=kf(d,nf,f)
60 j=nop(c)
goto (20, 70, 75, 75, 60, 75, 60, 75) (j-39)
65 e=fp(nf,f)
return
70 h=fp(nf,f)
goto 31
75 ni=ki(j,ni,i)
goto 30
end
function kf(v,n,f)
real f(24)
kf=n+1
f(n+1)=v
return
end
function ki(j,n,i)
integer i(24)
ki=n+1
i(n+1)=j
return
end
function fp(n,f)
real f(24)
fp=f(n)
n=n-1
return
end
function ip(n,i)
integer i(24)
ip=i(n)
n=n-1
return
end
function nop(s)
character*99 s
l=1
do while(s(l:l).eq." ".and.l.lt.99)
l=l+1
enddo
nop=ichar(s(l:l))
s(l:l)=" "
return
end
function isp(s)
character*99 s
isp=0
l=1
do while(s(l:l).eq." ".and.l.lt.99)
l=l+1
enddo
isp=41-ichar(s(l:l))
if (isp.eq.1) s(l:l)=" "
return
end
function fr(s)
character*99 s
m=1
n=1
i=1
do while(i.le.99)
j=ichar(s(i:i))
if (j.eq.32) goto 90
if (j.ge.48.and.j.lt.58) goto 89
if (j.eq.43.or.j.eq.45) goto (89,80) m
if (j.eq.46) goto (83,80) n
80 exit
83 n=2
89 m=2
90 i=i+1
enddo
read(s(1:i-1),*) fr
do 91 j=1,i-1
s(j:j)=" "
91 continue
return
end
Clear version: (3340 characters with scaffold)
program infixeval
character*99 c
do while (.true.)
do 10 i=1,99
c(i:i)=" "
10 continue
read(*,"(A99)") c
f=e(c)
write(*,*)f
enddo
end
function e(c)
character*99 c
character b
real f(24) ! value stack
integer i(24) ! operator stack
nf=0 ! number of items on the value stack
ni=0 ! number of items on the operator stack
20 nf=pushf(0.0,nf,f)
ni=pushi(43,ni,i) ! ichar(+) = 43
D write (*,*) "'",c,"'"
30 if (isp(c).eq.1) goto 20
h=fr(c)
D write (*,*) "'",c,"'"
31 g=fpop(nf,f)
j=ipop(ni,i)
D write(*,*) "Opperate ",g," ",char(j)," ",h
select case(j)
case (40)
goto 20
case (42) ! "*"
d=g*h
case (43) ! "+"
d=g+h
case (45) ! "-"
d=g-h
case (47) ! "*"
d=g/h
end select
50 nf=pushf(d,nf,f)
60 j=nop(c)
D write(*,*) "Got op: ", char(j)
goto (20, 70, 75, 75, 60, 75, 60, 75) (j-39)
65 e=fpop(nf,f)
return
70 h=fpop(nf,f) ! Encountered a "("
goto 31
75 ni=pushi(j,ni,i)
goto 30
end
c push onto a real stack
c OB as kf
function pushf(v,n,f)
real f(24)
pushf=n+1
f(n+1)=v
D write(*,*) "Push ", v
return
end
c push onto a integer stack
c OB as ki
function pushi(j,n,i)
integer i(24)
pushi=n+1
i(n+1)=j
D write(*,*) "Push ", char(j)
return
end
c pop from real stack
c OB as fp
function fpop(n,f)
real f(24)
fpop=f(n)
n=n-1
D write (*,*) "Pop ", fpop
return
end
c pop from integer stack
c OB as ip
function ipop(n,i)
integer i(24)
ipop=i(n)
n=n-1
D write (*,*) "Pop ", char(ipop)
return
end
c Next OPerator: returns the next nonws character, and removes it
c from the string
function nop(s)
character*99 s
l=1
do while(s(l:l).eq." ".and.l.lt.99)
l=l+1
enddo
nop=ichar(s(l:l))
s(l:l)=" "
return
end
c IS an open Paren: return 1 if the next non-ws character is "("
c (also overwrite it with a space. Otherwise return not 1
function isp(s)
character*99 s
isp=0
l=1
do while(s(l:l).eq." ".and.l.lt.99)
l=l+1
enddo
isp=41-ichar(s(l:l))
if (isp.eq.1) s(l:l)=" "
return
end
c Float Read: return the next real number in the string and removes the
c character
function fr(s)
character*99 s
m=1 ! No sign (Minus or plus) so far
n=1 ! No decimal so far
i=1
do while(i.le.99)
j=ichar(s(i:i))
if (j.eq.32) goto 90 ! skip spaces
if (j.ge.48.and.j.lt.58) goto 89
if (j.eq.43.or.j.eq.45) goto (89,80) m
if (j.eq.46) goto (83,80) n
c not part of a number
80 exit
83 n=2
89 m=2
90 i=i+1
enddo
read(s(1:i-1),*) fr
do 91 j=1,i-1
s(j:j)=" "
91 continue
return
end
Notes This edited version is rather more evil than my first attempt. Same algorithm, but now inline with a horrible tangle of gotos. I've ditched the co-routines, but am now using a couple of flavors of computed branches. All error checking and reporting has been removed, but this version will silently recover from some classes of unexpected characters in the input. This version also compiles with g77.
The primary limits are still fortran's rigid formatting, long and ubiquitous keywords, and simple primitives.
C99
Number of characters: 239 (But see below for 209)
compressed function:
#define S while(*e==32)++e
#define F float
F strtof();char*e;F v();F g(){S;return*e++-40?strtof(e-1,&e):v();}F v(){F b,a=g();for(;;){S;F o=*e++;if(!o|o==41)return a;b=g();a=o==43?a+b:o==45?a-b:o==42?a*b:a/b;}}F f(char*x){e=x;return v();}
decompressed function:
float strtof();
char* e;
float v();
float g() {
while (*e == ' ') ++e;
return *e++ != '(' ? strtof(e-1, &e) : v();
}
float v() {
float b, a = g();
for (;;) {
while (*e == ' ') ++e;
float op = *e++;
if (op == 0 || op == ')') return a;
b = g();
a = op == '+' ? a + b : op == '-' ? a - b : op == '*' ? a * b : a / b;
}
}
float eval(char* x) {
e = x;
return v();
}
Function is not re-entrant.
EDIT from Chris Lutz: I hate to trample on another man's code, but here is a 209-character version:
#define S for(;*e==32;e++)
#define X (*e++-40?strtof(e-1,&e):v())
float strtof();char*e;float v(){float o,a=X;for(;;){S;o=*e++;if(!o|o==41)return a;S;a=o-43?o-45?o-42?a/X:a*X:a-X:a+X;}}
#define f(x) (e=x,v())
Readable (well, not really very readable, but decompressed):
float strtof();
char *e;
float v() {
float o, a = *e++ != '(' ? strtof(e - 1, &e) : v();
for(;;) {
for(; *e == ' '; e++);
o = *e++;
if(o == 0 || o==')') return a;
for(; *e == ' '; e++);
// I have no idea how to properly indent nested conditionals
// and this is far too long to fit on one line.
a = o != '+' ?
o != '-' ?
o != '*' ?
a / (*e++ != '(' ? strtof(e - 1, &e) : v()) :
a * (*e++ != '(' ? strtof(e - 1, &e) : v()) :
a - (*e++ != '(' ? strtof(e - 1, &e) : v()) :
a + (*e++ != '(' ? strtof(e - 1, &e) : v());
}
}
#define f(x) (e = x, v())
Yeah, f() is a macro, not a function, but it works. The readable version has some of the logic rewritten but not reordered (like o != '+' instead of o - '+'), but is otherwise just an indented (and preprocessed) version of the other one. I keep trying to simplify the if(!o|o==41)return a; part into the for() loop, but it never makes it shorter. I still believe it can be done, but I'm done golfing. If I work on this question anymore, it will be in the language that must not be named.
Common Lisp
(SBCL)
Number of characters: 251
(defun g(e)(if(numberp e)e(let((m (g (pop e)))(o(loop for x in e by #'cddr collect x))(n(loop for x in (cdr e)by #'cddr collect (g x))))(mapcar(lambda(x y)(setf m(apply x(list m y))))o n)m)))(defun w(e)(g(read-from-string(concatenate'string"("e")"))))
Proper version (387 chars):
(defun wrapper (exp) (golf-eval (read-from-string (concatenate 'string "(" exp ")"))))
(defun golf-eval (exp)
(if (numberp exp)
exp
(let ((mem (golf-eval (pop exp)))
(op-list (loop for x in exp by #'cddr collect x))
(num-list (loop for x in (cdr exp) by #'cddr collect (golf-eval x))))
(mapcar (lambda (x y) (setf mem (apply x (list mem y)))) op-list num-list)
mem)))
Input is form w(), which takes one string argument. It uses the trick that nums/operands and operators are in the pattern N O N O N ... and recursively evaluates all operands, and therefore getting nesting very cheap. ;)
JavaScript (Not IE compatible)
Number of characters: 268/260
Fully obfuscated function:
function e(x){x=x.replace(/ /g,'')+')'
function P(n){return x[0]=='('?(x=x.substr(1),E()):(n=/^[-+]?[\d.]+/(x)[0],x=x.substr(n.length),+n)}function E(a,o,b){a=P()
for(;;){o=x[0]
x=x.substr(1)
if(o==')')return a
b=P()
a=o=='+'?a+b:o=='-'?a-b:o=='*'?a*b:a/b}}return E()}
or, in JavaScript 1.8 (Firefox 3+), you can save a few characters by using expression closures:
e=function(x,P,E)(x=x.replace(/ /g,'')+')',P=function(n)(x[0]=='('?(x=x.substr(1),E()):(n=/^[-+]?[\d.]+/(x)[0],x=x.substr(n.length),+n)),E=function(a,o,b){a=P()
for(;;){o=x[0]
x=x.substr(1)
if(o==')')return a
b=P()
a=o=='+'?a+b:o=='-'?a-b:o=='*'?a*b:a/b}},E())
Clear/semi-obfuscated function:
function evaluate(x) {
x = x.replace(/ /g, "") + ")";
function primary() {
if (x[0] == '(') {
x = x.substr(1);
return expression();
}
var n = /^[-+]?\d*\.?\d*/.exec(x)[0];
x = x.substr(n.length);
return +n;
}
function expression() {
var a = primary();
for (;;) {
var operator = x[0];
x = x.substr(1);
if (operator == ')') {
return a;
}
var b = primary();
a = (operator == '+') ? a + b :
(operator == '-') ? a - b :
(operator == '*') ? a * b :
a / b;
}
}
return expression();
}
Neither version will work in IE, because they use array-style subscripting on the string. If you replace both occurrences of x[0] with x.charAt(0), the first one should work everywhere.
I cut out some more characters since the first version by turning variables into function parameters and replacing another if statement with the conditional operator.
C# with Regex Love
Number of characters: 384
Fully-obfuscated:
float E(string i){i=i.Replace(" ","");Regex b=new Regex(#"\((?>[^()]+|\((?<D>)|\)(?<-D>))*(?(D)(?!))\)");i=b.Replace(i,m=>Eval(m.Value.Substring(1,m.Length-2)).ToString());float r=0;foreach(Match m in Regex.Matches(i,#"(?<=^|\D)-?[\d.]+")){float f=float.Parse(m.Value);if(m.Index==0)r=f;else{char o=i[m.Index-1];if(o=='+')r+=f;if(o=='-')r-=f;if(o=='*')r*=f;if(o=='/')r/=f;}}return r;}
Not-obfuscated:
private static float Eval(string input)
{
input = input.Replace(" ", "");
Regex balancedMatcher = new Regex(#"\(
(?>
[^()]+
|
\( (?<Depth>)
|
\) (?<-Depth>)
)*
(?(Depth)(?!))
\)", RegexOptions.IgnorePatternWhitespace);
input = balancedMatcher.Replace(input, m => Eval(m.Value.Substring(1, m.Length - 2)).ToString());
float result = 0;
foreach (Match m in Regex.Matches(input, #"(?<=^|\D)-?[\d.]+"))
{
float floatVal = float.Parse(m.Value);
if (m.Index == 0)
{
result = floatVal;
}
else
{
char op = input[m.Index - 1];
if (op == '+') result += floatVal;
if (op == '-') result -= floatVal;
if (op == '*') result *= floatVal;
if (op == '/') result /= floatVal;
}
}
return result;
}
Takes advantage of .NET's Regex balancing group feature.
PHP
Number of characters: 284
obfuscated:
function f($m){return c($m[1]);}function g($n,$m){$o=$m[0];$m[0]=' ';return$o=='+'?$n+$m:($o=='-'?$n-$m:($o=='*'?$n*$m:$n/$m));}function c($s){while($s!=($t=preg_replace_callback('/\(([^()]*)\)/',f,$s)))$s=$t;preg_match_all('![-+/*].*?[\d.]+!',"+$s",$m);return array_reduce($m[0],g);}
readable:
function callback1($m) {return c($m[1]);}
function callback2($n,$m) {
$o=$m[0];
$m[0]=' ';
return $o=='+' ? $n+$m : ($o=='-' ? $n-$m : ($o=='*' ? $n*$m : $n/$m));
}
function c($s){
while ($s != ($t = preg_replace_callback('/\(([^()]*)\)/','callback1',$s))) $s=$t;
preg_match_all('![-+/*].*?[\d.]+!', "+$s", $m);
return array_reduce($m[0], 'callback2');
}
$str = ' 2.45/8.5 * -9.27 + ( 5 * 0.0023 ) ';
var_dump(c($str));
# float(-2.66044117647)
Should work with any valid input (including negative numbers and arbitrary whitespace)
SQL (SQL Server 2008)
Number of characters: 4202
Fully obfuscated function:
WITH Input(id,str)AS(SELECT 1,'1 + 3 / -8'UNION ALL SELECT 2,'2*3*4*5+99'UNION ALL SELECT 3,'4 * (9 - 4)/ (2 * 6 - 2)+ 8'UNION ALL SELECT 4,'1 + ((123 * 3 - 69)/ 100)'UNION ALL SELECT 5,'2.45/8.5*9.27+(5*0.0023)'),Separators(i,ch,str_src,priority)AS(SELECT 1,'-',1,1UNION ALL SELECT 2,'+',1,1UNION ALL SELECT 3,'*',1,1UNION ALL SELECT 4,'/',1,1UNION ALL SELECT 5,'(',0,0UNION ALL SELECT 6,')',0,0),SeparatorsStrSrc(str,i)AS(SELECT CAST('['AS varchar(max)),0UNION ALL SELECT str+ch,SSS.i+1FROM SeparatorsStrSrc SSS INNER JOIN Separators S ON SSS.i=S.i-1WHERE str_src<>0),SeparatorsStr(str)AS(SELECT str+']'FROM SeparatorsStrSrc WHERE i=(SELECT COUNT(*)FROM Separators WHERE str_src<>0)),ExprElementsSrc(id,i,tmp,ele,pre_ch,input_str)AS(SELECT id,1,CAST(LEFT(str,1)AS varchar(max)),CAST(''AS varchar(max)),CAST(' 'AS char(1)),SUBSTRING(str,2,LEN(str))FROM Input UNION ALL SELECT id,CASE ele WHEN''THEN i ELSE i+1 END,CAST(CASE WHEN LEFT(input_str,1)=' 'THEN''WHEN tmp='-'THEN CASE WHEN pre_ch LIKE(SELECT str FROM SeparatorsStr)THEN tmp+LEFT(input_str,1)ELSE LEFT(input_str,1)END WHEN LEFT(input_str,1)IN(SELECT ch FROM Separators)OR tmp IN(SELECT ch FROM Separators)THEN LEFT(input_str,1)ELSE tmp+LEFT(input_str,1)END AS varchar(max)),CAST(CASE WHEN LEFT(input_str,1)=' 'THEN tmp WHEN LEFT(input_str,1)='-'THEN CASE WHEN tmp IN(SELECT ch FROM Separators)THEN tmp ELSE''END WHEN LEFT(input_str,1)IN(SELECT ch FROM Separators)OR tmp IN(SELECT ch FROM Separators)THEN CASE WHEN tmp='-'AND pre_ch LIKE(SELECT str FROM SeparatorsStr)THEN''ELSE tmp END ELSE''END AS varchar(max)),CAST(LEFT(ele,1)AS char(1)),SUBSTRING(input_str,2,LEN(input_str))FROM ExprElementsSrc WHERE input_str<>''OR tmp<>''),ExprElements(id,i,ele)AS(SELECT id,i,ele FROM ExprElementsSrc WHERE ele<>''),Scanner(id,i,val)AS(SELECT id,i,CAST(ele AS varchar(max))FROM ExprElements WHERE ele<>''UNION ALL SELECT id,MAX(i)+1,NULL FROM ExprElements GROUP BY id),Operator(op,priority)AS(SELECT ch,priority FROM Separators WHERE priority<>0),Calc(id,c,i,pop_count,s0,s1,s2,stack,status)AS(SELECT Scanner.id,1,1,0,CAST(scanner.val AS varchar(max)),CAST(NULL AS varchar(max)),CAST(NULL AS varchar(max)),CAST(''AS varchar(max)),CAST('init'AS varchar(max))FROM Scanner WHERE Scanner.i=1UNION ALL SELECT Calc.id,Calc.c+1,Calc.i,3,NULL,NULL,NULL,CASE Calc.s1 WHEN'+'THEN CAST(CAST(Calc.s2 AS real)+CAST(Calc.s0 AS real)AS varchar(max))WHEN'-'THEN CAST(CAST(Calc.s2 AS real)-CAST(Calc.s0 AS real)AS varchar(max))WHEN'*'THEN CAST(CAST(Calc.s2 AS real)*CAST(Calc.s0 AS real)AS varchar(max))WHEN'/'THEN CAST(CAST(Calc.s2 AS real)/CAST(Calc.s0 AS real)AS varchar(max))ELSE NULL END+' '+stack,CAST('calc '+Calc.s1 AS varchar(max))FROM Calc INNER JOIN Scanner NextVal ON Calc.id=NextVal.id AND Calc.i+1=NextVal.i WHERE Calc.pop_count=0AND ISNUMERIC(Calc.s2)=1AND Calc.s1 IN(SELECT op FROM Operator)AND ISNUMERIC(Calc.s0)=1AND(SELECT priority FROM Operator WHERE op=Calc.s1)>=COALESCE((SELECT priority FROM Operator WHERE op=NextVal.val),0)UNION ALL SELECT Calc.id,Calc.c+1,Calc.i,3,NULL,NULL,NULL,s1+' '+stack,CAST('paren'AS varchar(max))FROM Calc WHERE pop_count=0AND s2='('AND ISNUMERIC(s1)=1AND s0=')'UNION ALL SELECT Calc.id,Calc.c+1,Calc.i,Calc.pop_count-1,s1,s2,CASE WHEN LEN(stack)>0THEN SUBSTRING(stack,1,CHARINDEX(' ',stack)-1)ELSE NULL END,CASE WHEN LEN(stack)>0THEN SUBSTRING(stack,CHARINDEX(' ',stack)+1,LEN(stack))ELSE''END,CAST('pop'AS varchar(max))FROM Calc WHERE Calc.pop_count>0UNION ALL SELECT Calc.id,Calc.c+1,Calc.i+1,Calc.pop_count,CAST(NextVal.val AS varchar(max)),s0,s1,coalesce(s2,'')+' '+stack,cast('read'as varchar(max))FROM Calc INNER JOIN Scanner NextVal ON Calc.id=NextVal.id AND Calc.i+1=NextVal.i WHERE NextVal.val IS NOT NULL AND Calc.pop_count=0AND((Calc.s0 IS NULL OR calc.s1 IS NULL OR calc.s2 IS NULL)OR NOT(ISNUMERIC(Calc.s2)=1AND Calc.s1 IN(SELECT op FROM Operator)AND ISNUMERIC(calc.s0)=1AND (SELECT priority FROM Operator WHERE op=Calc.s1)>=COALESCE((SELECT priority FROM Operator WHERE op=NextVal.val),0))AND NOT(s2='('AND ISNUMERIC(s1)=1AND s0=')')))SELECT Calc.id,Input.str,Calc.s0 AS result FROM Calc INNER JOIN Input ON Calc.id=Input.id WHERE Calc.c=(SELECT MAX(c)FROM Calc calc2 WHERE Calc.id=Calc2.id)ORDER BY id
Clear/semi-obfuscated function:
WITH
Input(id, str) AS (
SELECT 1, '1 + 3 / -8'
UNION ALL SELECT 2, '2*3*4*5+99'
UNION ALL SELECT 3, '4 * (9 - 4) / (2 * 6 - 2) + 8'
UNION ALL SELECT 4, '1 + ((123 * 3 - 69) / 100)'
UNION ALL SELECT 5, '2.45/8.5*9.27+(5*0.0023)'
)
, Separators(i, ch, str_src, priority) AS (
SELECT 1, '-', 1, 1
UNION ALL SELECT 2, '+', 1, 1
UNION ALL SELECT 3, '*', 1, 1
UNION ALL SELECT 4, '/', 1, 1
UNION ALL SELECT 5, '(', 0, 0
UNION ALL SELECT 6, ')', 0, 0
)
, SeparatorsStrSrc(str, i) AS (
SELECT CAST('[' AS varchar(max)), 0
UNION ALL
SELECT
str + ch
, SSS.i + 1
FROM
SeparatorsStrSrc SSS
INNER JOIN Separators S ON SSS.i = S.i - 1
WHERE
str_src <> 0
)
, SeparatorsStr(str) AS (
SELECT str + ']' FROM SeparatorsStrSrc
WHERE i = (SELECT COUNT(*) FROM Separators WHERE str_src <> 0)
)
, ExprElementsSrc(id, i, tmp, ele, pre_ch, input_str) AS (
SELECT
id
, 1
, CAST(LEFT(str, 1) AS varchar(max))
, CAST('' AS varchar(max))
, CAST(' ' AS char(1))
, SUBSTRING(str, 2, LEN(str))
FROM
Input
UNION ALL
SELECT
id
, CASE ele
WHEN '' THEN i
ELSE i + 1
END
, CAST(
CASE
WHEN LEFT(input_str, 1) = ' '
THEN ''
WHEN tmp = '-'
THEN CASE
WHEN pre_ch LIKE (SELECT str FROM SeparatorsStr)
THEN tmp + LEFT(input_str, 1)
ELSE LEFT(input_str, 1)
END
WHEN LEFT(input_str, 1) IN (SELECT ch FROM Separators)
OR
tmp IN (SELECT ch FROM Separators)
THEN LEFT(input_str, 1)
ELSE tmp + LEFT(input_str, 1)
END
AS varchar(max))
, CAST(
CASE
WHEN LEFT(input_str, 1) = ' '
THEN tmp
WHEN LEFT(input_str, 1) = '-'
THEN CASE
WHEN tmp IN (SELECT ch FROM Separators)
THEN tmp
ELSE ''
END
WHEN LEFT(input_str, 1) IN (SELECT ch FROM Separators)
OR
tmp IN (SELECT ch FROM Separators)
THEN CASE
WHEN tmp = '-' AND pre_ch LIKE (SELECT str FROM SeparatorsStr)
THEN ''
ELSE tmp
END
ELSE ''
END
AS varchar(max))
, CAST(LEFT(ele, 1) AS char(1))
, SUBSTRING(input_str, 2, LEN(input_str))
FROM
ExprElementsSrc
WHERE
input_str <> ''
OR
tmp <> ''
)
, ExprElements(id, i, ele) AS (
SELECT
id
, i
, ele
FROM
ExprElementsSrc
WHERE
ele <> ''
)
, Scanner(id, i, val) AS (
SELECT
id
, i
, CAST(ele AS varchar(max))
FROM
ExprElements
WHERE
ele <> ''
UNION ALL
SELECT
id
, MAX(i) + 1
, NULL
FROM
ExprElements
GROUP BY
id
)
, Operator(op, priority) AS (
SELECT
ch
, priority
FROM
Separators
WHERE
priority <> 0
)
, Calc(id, c, i, pop_count, s0, s1, s2, stack, status) AS (
SELECT
Scanner.id
, 1
, 1
, 0
, CAST(scanner.val AS varchar(max))
, CAST(NULL AS varchar(max))
, CAST(NULL AS varchar(max))
, CAST('' AS varchar(max))
, CAST('init' AS varchar(max))
FROM
Scanner
WHERE
Scanner.i = 1
UNION ALL
SELECT
Calc.id
, Calc.c + 1
, Calc.i
, 3
, NULL
, NULL
, NULL
, CASE Calc.s1
WHEN '+' THEN CAST(CAST(Calc.s2 AS real) + CAST(Calc.s0 AS real) AS varchar(max))
WHEN '-' THEN CAST(CAST(Calc.s2 AS real) - CAST(Calc.s0 AS real) AS varchar(max))
WHEN '*' THEN CAST(CAST(Calc.s2 AS real) * CAST(Calc.s0 AS real) AS varchar(max))
WHEN '/' THEN CAST(CAST(Calc.s2 AS real) / CAST(Calc.s0 AS real) AS varchar(max))
ELSE NULL
END
+ ' '
+ stack
, CAST('calc ' + Calc.s1 AS varchar(max))
FROM
Calc
INNER JOIN Scanner NextVal ON Calc.id = NextVal.id
AND Calc.i + 1 = NextVal.i
WHERE
Calc.pop_count = 0
AND ISNUMERIC(Calc.s2) = 1
AND Calc.s1 IN (SELECT op FROM Operator)
AND ISNUMERIC(Calc.s0) = 1
AND (SELECT priority FROM Operator WHERE op = Calc.s1)
>= COALESCE((SELECT priority FROM Operator WHERE op = NextVal.val), 0)
UNION ALL
SELECT
Calc.id
, Calc.c + 1
, Calc.i
, 3
, NULL
, NULL
, NULL
, s1 + ' ' + stack
, CAST('paren' AS varchar(max))
FROM
Calc
WHERE
pop_count = 0
AND s2 = '('
AND ISNUMERIC(s1) = 1
AND s0 = ')'
UNION ALL
SELECT
Calc.id
, Calc.c + 1
, Calc.i
, Calc.pop_count - 1
, s1
, s2
, CASE
WHEN LEN(stack) > 0
THEN SUBSTRING(stack, 1, CHARINDEX(' ', stack) - 1)
ELSE NULL
END
, CASE
WHEN LEN(stack) > 0
THEN SUBSTRING(stack, CHARINDEX(' ', stack) + 1, LEN(stack))
ELSE ''
END
, CAST('pop' AS varchar(max))
FROM
Calc
WHERE
Calc.pop_count > 0
UNION ALL
SELECT
Calc.id
, Calc.c + 1
, Calc.i + 1
, Calc.pop_count
, CAST(NextVal.val AS varchar(max))
, s0
, s1
, coalesce(s2, '') + ' ' + stack
, cast('read' as varchar(max))
FROM
Calc
INNER JOIN Scanner NextVal ON Calc.id = NextVal.id
AND Calc.i + 1 = NextVal.i
WHERE
NextVal.val IS NOT NULL
AND Calc.pop_count = 0
AND (
(Calc.s0 IS NULL or calc.s1 is null or calc.s2 is null)
OR
NOT(
ISNUMERIC(Calc.s2) = 1
AND Calc.s1 IN (SELECT op FROM Operator)
AND ISNUMERIC(calc.s0) = 1
AND (SELECT priority FROM Operator WHERE op = Calc.s1)
>= COALESCE((SELECT priority FROM Operator WHERE op = NextVal.val), 0)
)
AND NOT(s2 = '(' AND ISNUMERIC(s1) = 1 AND s0 = ')')
)
)
SELECT
Calc.id
, Input.str
, Calc.s0 AS result
FROM
Calc
INNER JOIN Input ON Calc.id = Input.id
WHERE
Calc.c = (SELECT MAX(c) FROM Calc calc2
WHERE Calc.id = Calc2.id)
ORDER BY
id
It is not shortest.
But I think that it is very flexible for SQL.
It's easy to add new operators.
It's easy to change priority of operators.
F#
Number of characters: 327
OP was looking for an F# version, here it is. Can be done a lot nicer since I'm abusing a ref here to save characters. It handles most things such as -(1.0), 3 - -3 and even 0 - .5 etc.
let g s=
let c=ref[for x in System.Text.RegularExpressions.Regex.Matches(s,"[0-9.]+|[^\s]")->x.Value]
let rec e v=if (!c).IsEmpty then v else
let h=(!c).Head
c:=(!c).Tail
match h with|"("->e(e 0.0)|")"->v|"+"->e(v+(e 0.0))|"-"->e(v-(e 0.0))|"/"->e(v/(e 0.0))|"*"->e(v*(e 0.0))|x->float x
e(e 0.0)
J
Number of characters: 208
After Jeff Moser's comment, I realized that I had completely forgotten about this language... I'm no expert, but my first attempt went rather well.
e=:>#{:#f#;:
f=:''&(4 :0)
'y x'=.x g y
while.($y)*-.')'={.>{.y do.'y x'=.(x,>(-.'/'={.>{.y){('%';y))g}.y end.y;x
)
g=:4 :0
z=.>{.y
if.z='('do.'y z'=.f}.y else.if.z='-'do.z=.'_',>{.}.y end.end.(}.y);":".x,z
)
It's a bit annoying, having to map x/y and -z into J's x%y and _z. Without that, maybe 50% of this code could disappear.
Python (without importing anything)
Number of characters: 222
I stole many tricks from Dave's answer, but I managed to shave off some more characters.
def e(s,l=0,n=0,f='+'):
if s:l=[c for c in s+')'if' '!=c]
while f!=')':
p=l.pop;m=p(0)
if m=='(':m=e(0,l)
while l[0]not in'+-*/)':m+=p(0)
m=float(m);n={'+':n+m,'-':n-m,'*':n*m,'/':n/(m or 1)}[f];f=p(0)
return n
Commented version:
def evaluate(stringexpr, listexpr=0, n=0, f_operation='+'):
# start out as taking 0 + the expression... (or could use 1 * ;)
# We'll prefer to keep the expression as a list of characters,
# so we can use .pop(0) to eat up the expression as we go.
if stringexpr:
listexpr = [c for c in stringexpr+')' if c!=' ']
# use ')' as sentinel to return the answer
while f_operation != ')':
m_next = listexpr.pop(0)
if m_next == '(':
# lists are passed by reference, so this call will eat the (parexp)
m_next = evaluate(None, listexpr)
else:
# rebuild any upcoming numeric chars into a string
while listexpr[0] not in '+-*/)':
m_next += listexpr.pop(0)
# Update n as the current answer. But never divide by 0.
m = float(m_next)
n = {'+':n+m, '-':n-m, '*':n*m, '/':n/(m or 1)}[f_operation]
# prepare the next operation (known to be one of '+-*/)')
f_operation = listexpr.pop(0)
return n
C#
Number of characters: 403
So here's my solution... I'm still waiting for someone to post one in C# that can beat it. (Marc Gravell was close, and may yet do better than me after some more tinkering.)
Fully obfuscated function:
float e(string x){float v=0;if(float.TryParse(x,out v))return v;x+=';';int t=0;
char o,s='?',p='+';float n=0;int l=0;for(int i=0;i<x.Length;i++){o=s;if(
x[i]!=' '){s=x[i];if(char.IsDigit(x[i])|s=='.'|(s=='-'&o!='1'))s='1';if(s==')')
l--;if(s!=o&l==0){if(o=='1'|o==')'){n=e(x.Substring(t,i-t));if(p=='+')v+=n;
if(p=='-')v-=n;if(p=='*')v*=n;if(p=='/')v/=n;p=x[i];}t=i;if(s=='(')t++;}
if(s=='(')l++;}}return v;}
Semi-obfuscated function:
public static float Eval(string expr)
{
float val = 0;
if (float.TryParse(expr, out val))
return val;
expr += ';';
int tokenStart = 0;
char oldState, state = '?', op = '+';
float num = 0;
int level = 0;
for (int i = 0; i < expr.Length; i++)
{
oldState = state;
if (expr[i] != ' ')
{
state = expr[i];
if (char.IsDigit(expr[i]) || state == '.' ||
(state == '-' && oldState != '1'))
state = '1';
if (state == ')')
level--;
if (state != oldState && level == 0)
{
if (oldState == '1' || oldState == ')')
{
num = Eval(expr.Substring(tokenStart, i - tokenStart));
if (op == '+') val += num;
if (op == '-') val -= num;
if (op == '*') val *= num;
if (op == '/') val /= num;
op = expr[i];
}
tokenStart = i;
if (state == '(')
tokenStart++;
}
if (state == '(')
level++;
}
}
return val;
}
Nothing too clever going on here, it woul seem. The function does however have the advantage of being re-entrant (i.e. thread-safe).
I am also reasonably pleased with the number of chars, given that it's written in C# (valid 1.0, 2.0, and 3.0 I believe).
Here comes another one:
Shell script (using sed+awk)
Number of characters: 295
obfuscated:
e(){ a="$1";while echo "$a"|grep -q \(;do eval "`echo "$a"|sed 's/\(.*\)(\([^()]*\))\(.*\)/a="\1\`e \"\2\"\`\3"/'`";done; echo "$a"|sed 's/\([-+*/]\) *\(-\?\) */ \1 \2/g'|awk '{t=$1;for(i=2;i<NF;i+=2){j=$(i+1);if($i=="+") t+=j; else if($i=="-") t-=j; else if($i=="*") t*=j; else t/=j}print t}';}
readable
e () {
a="$1"
# Recursively process bracket-expressions
while echo "$a"|grep -q \(; do
eval "`echo "$a"|
sed 's/\(.*\)(\([^()]*\))\(.*\)/a="\1\`e \"\2\"\`\3"/'`"
done
# Compute expression without brackets
echo "$a"|
sed 's/\([-+*/]\) *\(-\?\) */ \1 \2/g'|
awk '{
t=$1;
for(i=2;i<NF;i+=2){
j=$(i+1);
if($i=="+") t+=j;
else if($i=="-") t-=j;
else if($i=="*") t*=j;
else t/=j
}
print t
}'
}
Test:
str=' 2.45 / 8.5 * 9.27 + ( 5 * 0.0023 ) '
echo "$str"|bc -l
e "$str"
Result:
2.68344117647058823526
2.68344
MATLAB (v7.8.0)
Number of characters: 239
Obfuscated function:
function [v,s]=m(s),r=1;while s,s=regexp(s,'( ?)(?(1)-?)[\.\d]+|\S','match');c=s{end};s=[s{1:end-1}];if any(c>47),v=str2num(c);elseif c>41,[l,s]=m(s);v=[l/v l*v l+v l-v];v=v(c=='/*+-');if r,break;end;r=1;elseif c<41,break;end;r=r&c~=41;end
Clear(er) function:
function [value,str] = math(str)
returnNow = 1;
while str,
str = regexp(str,'( ?)(?(1)-?)[\.\d]+|\S','match');
current = str{end};
str = [str{1:end-1}];
if any(current > 47),
value = str2num(current);
elseif current > 41,
[leftValue,str] = math(str);
value = [leftValue/value leftValue*value ...
leftValue+value leftValue-value];
value = value(current == '/*+-');
if returnNow,
break;
end;
returnNow = 1;
elseif current < 41,
break;
end;
returnNow = returnNow & (c ~= 41);
end
Test:
>> [math('1 + 3 / -8'); ...
math('2*3*4*5+99'); ...
math('4 * (9 - 4) / (2 * 6 - 2) + 8'); ...
math('1 + ((123 * 3 - 69) / 100)'); ...
math('2.45/8.5*9.27+(5*0.0023)')]
ans =
-0.5000
219.0000
10.0000
4.0000
2.6834
Synopsis: A mixture of regular expressions and recursion. Pretty much the best I have been able to do so far, without cheating and using EVAL.
Ruby
Number of characters: 170
Obfuscated:
def s(x)
while x.sub!(/\(([^\(\)]*?)\)/){s($1)}
x.gsub!('--','')
end
while x.sub!(/(-?[\d.]+)[ ]*([+\-*\/])[ ]*(-?[\d.]+)/){$1.to_f.send($2,$3.to_f)}
end
x.strip.to_f
end
Readable:
def s(x)
while x.sub!(/\(([^\(\)]*?)\)/){s($1)}
x.gsub!('--','')
end
while x.sub!(/(-?[\d.]+)[ ]*([+\-*\/])[ ]*(-?[\d.]+)/){$1.to_f.send($2,$3.to_f)}
end
x.strip.to_f
end
[
['1 + 3 / -8', -0.5],
['2*3*4*5+99', 219],
['4 * (9 - 4) / (2 * 6 - 2) + 8', 10],
['1 + ((123 * 3 - 69) / 100)', 4],
['2.45/8.5*9.27+(5*0.0023)',2.68344117647059],
['(3+7) - (5+2)', 3]
].each do |pair|
a,b = s(String.new(pair[0])),pair[1]
print pair[0].ljust(25), ' = ', b, ' (', a==b, ')'
puts
end
There is no real obfuscation to this one, which I decided to post fresh since it's wildly different from my first. I should have seen this from the start. The process is a very simple process of elimination: find and resolve the highest pair of parenthesis (the most nested) into a number until no more are found, then resolve all the existing numbers and operations into the result. And, while resolving parenthetical statements I have it strip all double-dashes (Float.to_f doesn't know what to do with them).
So, it supports positive and negative numbers (+3, 3, & -3) and even negated sub-expressions within the parenthesis just by the order of processing. The only shorter implementation is the Perl (w/o eval) one.
Edit: I'm still chasing Perl, but this is the second smallest answer right now. I shrunk it with changes to the second regex and by changing the treatment of the string to be destructive (replaces the old string). This eliminated the need to duplicate the string, which I found out to just be a new pointer to the string. And renaming the function to s from solve saved a few characters.
Python with regular expressions
Number of characters: 283
Fully obfuscated function:
import re
from operator import*
def c(e):
O=dict(zip("+-/*()",(add,sub,truediv,mul)))
a=[add,0];s=a
for v,o in re.findall("(-?[.\d]+)|([+-/*()])",e):
if v:s=[float(v)]+s
elif o=="(":s=a+s
elif o!=")":s=[O[o]]+s
if v or o==")":s[:3]=[s[1](s[2],s[0])]
return s[0]
Not obfuscated:
import re
from operator import *
def compute(s):
operators = dict(zip("+-/*()", (add, sub, truediv, mul)))
stack = [add, 0]
for val, op in re.findall("(-?[.\d]+)|([+-/*()])", s):
if val:
stack = [float(val)] + stack
elif op == "(":
stack = [add, 0] + stack
elif op != ")":
stack = [operators[op]] + stack
if val or op == ")":
stack[:3] = [stack[1](stack[2], stack[0])]
return stack[0]
I wanted to see if I cab beat the other Python solutions using regular expressions.
Couldn't.
The regular expression I'm using creates a list of pairs (val, op) where only one item in each pair is valid. The rest of the code is a rather standard stack based parser with a neat trick of replacing the top 3 cells in the stack with the result of the computation using Python list assignment syntax. Making this work with negative numbers required only two additional characters (-? in the regex).
Python
Number of characters: 382
Yet another Python solution, heavily using regular expression replacement. Each run through the loop the simplest expressions are computed and the results are put back into the string.
This is the unobfuscated code, unless you consider regular expressions to be obfuscated.
import re
from operator import *
operators = dict(zip("+-/*", (add, sub, truediv, mul)))
def compute(s):
def repl(m):
v1, op, v2 = m.groups()
return str(operators[op](float(v1), float(v2)))
while not re.match("^\d+\.\d+$", s):
s = re.sub("([.\d]+)\s*([+-/*])\s*([.\d]+)", repl, s)
s = re.sub("\(([.\d]+)\)", r"\1", s)
return s
Had this idea just as I was turning in and couldn't let it go until I wrote it down and made it work.
C#
Number of characters: 396 (updated)
(but fails the test you added with "/ -8", and I'm not inclined to fix it...
static float Eval(string s){int i,j;s=s.Trim();while((i=s.IndexOf(')'))>=0){j=s.LastIndexOf('(',i,i);s=s.Substring(0,j++)+Eval(s.Substring(j,i-j))+s.Substring(i+1);}if((i=s.LastIndexOfAny("+-*/".ToCharArray()))<0) return float.Parse(s);var r=float.Parse(s.Substring(i+1));var l=i>0?Eval(s.Substring(0,i)):(float?)null;return s[i]=='+'?(l??0)+r:(s[i]=='-'?(l??0)-r:(s[i]=='/'?(l??1)/r:(l??1)*r));}
From:
static float Eval(string s)
{
int i, j;
s = s.Trim();
while ((i = s.IndexOf(')')) >= 0)
{
j = s.LastIndexOf('(', i, i);
s = s.Substring(0, j++) + Eval(s.Substring(j, i - j)) + s.Substring(i + 1);
}
if ((i = s.LastIndexOfAny("+-*/".ToCharArray())) < 0) return float.Parse(s);
var r = float.Parse(s.Substring(i + 1));
var l = i > 0 ? Eval(s.Substring(0, i)) : (float?)null;
return s[i] == '+'
? (l ?? 0) + r
: (s[i] == '-'
? (l ?? 0) - r
: (s[i] == '/'
? (l ?? 1) / r
: (l ?? 1) * r));
}
Python
Number of characters: 235
Fully obfuscated function:
def g(a):
i=len(a)
while i:
try:m=g(a[i+1:]);n=g(a[:i]);a=str({'+':n+m,'-':n-m,'*':n*m,'/':n/(m or 1)}[a[i]])
except:i-=1;j=a.rfind('(')+1
if j:k=a.find(')',j);a=a[:j-1]+str(g(a[j:k]))+a[k+1:]
return float(a.replace('--',''))
Semi-obfuscated:
def g(a):
i=len(a);
# do the math
while i:
try:
# recursively evaluate left and right
m=g(a[i+1:])
n=g(a[:i])
# try to do the math assuming that a[i] is an operator
a=str({'+':n+m,'-':n-m,'*':n*m,'/':n/(m or 1)}[a[i]])
except:
# failure -> next try
i-=1
j=a.rfind('(')+1
# replace brackets in parallel (this part is executed first)
if j:
k=a.find(')',j)
a=a[:j-1]+str(g(a[j:k]))+a[k+1:]
return float(a.replace('--',''))
FWIW, the n+1th Python solution. In a blatant abuse of try-except I use a trial-and-error approach. It should handle all cases properly including stuff like -(8), --8 and g('-(1 - 3)'). It is re-entrant. Without support for the -- case which many implementations don't support, it is at 217 chars (see previous revision).
Thanks for an interesting hour on a Sunday and another 30 mins on Monday. Thanks to krubo for his nice dict.
Ruby
Number of characters: 217 179
This is the shortest ruby solution up to now (one heavily based on RegExp yields incorrect answers when string contains few groups of parenthesis) -- no longer true. Solutions based on regex and substitution are shorter. This one is based on stack of accumulators and parses whole expression from left to right. It is re-entrant, and does not modify input string. It could be accused of breaking the rules of not using eval, as it calls Float's methods with identical names as their mathematical mnemonics (+,-,/,*).
Obfuscated code (old version, tweaked below):
def f(p);a,o=[0],['+']
p.sub(/-/,'+-').scan(/(?:(-?\d+(?:\.\d+)?)|(.))\s*/).each{|n|
q,w=n;case w;when'(';a<<0;o<<'+';when')';q=a.pop;else;o<<w
end if q.nil?;a[-1]=a[-1].method(o.pop).call(q.to_f) if !q.nil?};a[0];end
More obfuscated code:
def f(p);a,o=[0],[:+]
p.scan(/(?:(-?\d+(?:\.\d+)?)|(.))\s*/).each{|n|q,w=n;case w
when'(';a<<0;o<<:+;when')';q=a.pop;else;o<<w;end if !q
a<<a.pop.send(o.pop,q.to_f)if q};a[0];end
Clean code:
def f(p)
accumulators, operands = [0], ['+']
p.gsub(/-/,'+-').scan(/(?:(-?\d+(?:\.\d+)?)|(.))\s*/).each do |n|
number, operand = n
case operand
when '('
accumulators << 0
operands << '+'
when ')'
number = accumulators.pop
operands.pop
else
operands[-1] = operand
end if number.nil?
accumulators[-1] = accumulators.last.method(operands[-1]).call(number.to_f) unless number.nil?
end
accumulators.first
end
Ruby 1.8.7
Number of characters: 620
Do try and take it easy on my implementation, it's the first time I've written an expression parser in my life! I guarantee that it isn't the best.
Obfuscated:
def solve_expression(e)
t,r,s,c,n=e.chars.to_a,[],'','',''
while(c=t.shift)
n=t[0]
if (s+c).match(/^(-?)[.\d]+$/) || (!n.nil? && n.match(/\d/) && c=='-')
s+=c
elsif (c=='-' && n=='(') || c=='('
m,o,x=c=='-',1,''
while(c=t.shift)
o+=1 if c=='('
o-=1 if c==')'
x+=c unless c==')' && o==0
break if o==0
end
r.push(m ? -solve_expression(x) : solve_expression(x))
s=''
elsif c.match(/[+\-\/*]/)
r.push(c) and s=''
else
r.push(s) if !s.empty?
s=''
end
end
r.push(s) unless s.empty?
i=1
a=r[0].to_f
while i<r.count
b,c=r[i..i+1]
c=c.to_f
case b
when '+': a=a+c
when '-': a=a-c
when '*': a=a*c
when '/': a=a/c
end
i+=2
end
a
end
Readable:
def solve_expression(expr)
chars = expr.chars.to_a # characters of the expression
parts = [] # resulting parts
s,c,n = '','','' # current string, character, next character
while(c = chars.shift)
n = chars[0]
if (s + c).match(/^(-?)[.\d]+$/) || (!n.nil? && n.match(/\d/) && c == '-') # only concatenate when it is part of a valid number
s += c
elsif (c == '-' && n == '(') || c == '(' # begin a sub-expression
negate = c == '-'
open = 1
subExpr = ''
while(c = chars.shift)
open += 1 if c == '('
open -= 1 if c == ')'
# if the number of open parenthesis equals 0, we've run to the end of the
# expression. Make a new expression with the new string, and add it to the
# stack.
subExpr += c unless c == ')' && open == 0
break if open == 0
end
parts.push(negate ? -solve_expression(subExpr) : solve_expression(subExpr))
s = ''
elsif c.match(/[+\-\/*]/)
parts.push(c) and s = ''
else
parts.push(s) if !s.empty?
s = ''
end
end
parts.push(s) unless s.empty? # expression exits 1 character too soon.
# now for some solutions!
i = 1
a = parts[0].to_f # left-most value is will become the result
while i < parts.count
b,c = parts[i..i+1]
c = c.to_f
case b
when '+': a = a + c
when '-': a = a - c
when '*': a = a * c
when '/': a = a / c
end
i += 2
end
a
end
Ruby 1.9
(because of the regex)
Number of characters: 296
def d(s)
while m = s.match(/((?<pg>\((?:\\[()]|[^()]|\g<pg>)*\)))/)
s.sub!(m[:pg], d(m[:pg][1,m[:pg].size-2]))
end
while m = s.match(/(-?\d+(\.\d+)?)\s*([*+\-\/])\s*(-?\d+(\.\d+)?)/)
r=m[1].to_f.send(m[3],m[4].to_f) if %w{+ - * /}.include?m[3]
s.sub!(m[0], r.to_s)
end
s
end
EDIT: Includes Martin's optimization.
SNOBOL4
Number of characters: 232
a = pos(0) | '('
n = span('0123456789.')
j = '!+;!-;!*;!/; output = e'
d j '!' len(1) . y = " e a . q n . l '" y "' n . r = q (l " y " r) :s(p)" :s(d)
k = code(j)
e = input
s e ' ' = :s(s)
p e ('(' n . i ')') = i :s(p)f<k>
end
This is a semi-cheat. It uses code() (a variant of eval) to de-compress itself, but not to evaluate the input expression.
De-obfuscated version, without code:
prefix = pos(0) | '('
num = span('0123456789.')
expr = input
spaces expr ' ' = '' :s(spaces)
paren expr ('(' num . x ')') = x :s(paren)
add expr (prefix . pfx) (num . l) '+' (num . r) = pfx (l + r) :s(paren)
sub expr (prefix . pfx) (num . l) '-' (num . r) = pfx (l - r) :s(paren)
mul expr (prefix . pfx) (num . l) '*' (num . r) = pfx (l * r) :s(paren)
div expr (prefix . pfx) (num . l) '/' (num . r) = pfx (l / r) :s(paren)
output = expr
end
Strategy:
First, remove all spaces (spaces)
Whenever possible, remove parentheses surrounding a number (paren)
Otherwise, find a simple expression involving two numbers, prefixed by '(' or at the start of the string
If none of the above rules apply, the expression is fully evaluated. Now if the input was well formed we should be left with a number.
Example:
1 + (2 * 3) + 4
1+(2*3)+4 [spaces]
1+(6)+4 [mul]
1+6+4 [paren]
7+4 [add]
11 [add]
C#
Number of Characters: 355
I took Noldorin's Answer and modified it, so give Noldorin 99% of the credit for this. Best I could do with the algorithm was using was 408 characters. See Noldorin's Answer for the clearer code version.
Changes made:
Change char comparisons to compare against numbers.
Removed some default declarations and combined same type of declarations.
Re-worked some of the if statments.
float q(string x){float v,n;if(!float.TryParse(x,out v)){x+=';';int t=0,l=0,i=0;char o,s='?',p='+';for(;i<x.Length;i++){o=s;if(x[i]!=32){s=x[i];if(char.IsDigit(x[i])|s==46|(s==45&o!=49))s='1';if(s==41)l--;if(s!=o&l==0){if(o==49|o==41){n=q(x.Substring(t,i-t));v=p==43?v+n:p==45?v-n:p==42?v*n:p==47?v/n:v;p=x[i];}t=i;if(s==40)t++;}if(s==40)l++;}}}return v;}
Edit: knocked it down some more, from 361 to 355, by removing one of the return statments.

Resources