Why does my list keep resetting itself in lisp? - lisp

I have an exercise that requires me to write a program for a hotel.
Everything is good but my guest list keeps resetting itself.
I have not implemented the other 2 options so here is all of my code
I've marked the problem with four semicolons.
(defun guest (_name _nbrguest _phone _duration _price _discount)
(setf (get _name 'nbrguest) _nbrguest)
(setf (get _name 'phone) _phone)
(setf (get _name 'duration) _duration)
(setf (get _name 'price) _price)
(setf (get _name 'discount) _discount)
(setf (get _name 'final_price) (- _price (discount _price _discount)))
(return-from guest _name))
(defun discount (original discounter)
(setq result (* (* original 1.0) (/ (* discounter 1.0) 100.0)))
(if (<= discounter 0)
(setq result original))
(return-from discount result))
(defun input_person ()
(format t "name: ")
(setq name (read))
(format t "Guest number: ")
(setq nbrguest (read))
(format t "Guest phone number: ")
(setq phone (read))
(format t "Stay duration: ")
(setq duration (read))
(format t "Room price: ")
(setq price (read))
(format t "Discount: ")
(setq discount (read))
(return-from input_person
(guest name nbrguest phone duration price discount)))
(defun hotel ()
(loop
(format t "1. Add guest~%")
(format t "2. Output guests~%")
(format t "3. Output guests who have paid more than input~%")
(format t "4. Remove guest~%")
(format t "5. Exit~%")
(format t "~%Enter choice: ")
(setq choice (read))
(setq _guests '())
(cond
((eq choice 5)
(return))
;;;; this part doesn't work as desired
((eq choice 1)
(if (null _guests)
(setq _guests (cons (input_person) _guests))
(setq _guests (append _guests (input_person)))))
;;;;
((eq choice 2)
(dolist (n _guests)
(princ n))))))
Can someone please explain the problem to me?
And how do I fix it?
Thank you!

In hotel, move ( setq _guests '() ) above (loop ....
Right now you're re-initializing your _guests variable on each iteration of the loop.
You'd see it much easier if you were using a saner more accepted line spacing style in your code, like this:
( defun guest ( _name _nbrguest _phone _duration _price _discount )
( setf ( get _name 'nbrguest ) _nbrguest )
( setf ( get _name 'phone ) _phone )
( setf ( get _name 'duration ) _duration )
( setf ( get _name 'price ) _price )
( setf ( get _name 'discount ) _discount )
( setf ( get _name 'final_price )
( - _price ( discount _price _discount ) ) )
( return-from guest _name ) )
( defun discount ( original discounter )
( setq result ( * ( * original 1.0 )
( / ( * discounter 1.0 ) 100.0 ) ) )
( if ( <= discounter 0 )
( setq result original ) )
( return-from discount result ) )
( defun input_person ( )
( format t "name: " )
( setq name ( read ) )
( format t "Guest number: " )
( setq nbrguest ( read ) )
( format t "Guest phone number: " )
( setq phone ( read ) )
( format t "Stay duration: " )
( setq duration ( read ) )
( format t "Room price: " )
( setq price ( read ) )
( format t "Discount: " )
( setq discount ( read ) )
( return-from input_person
( guest name nbrguest phone duration price discount ) ) )
( defun hotel ( )
( loop
( format t "1. Add guest~%" )
( format t "2. Output guests~%" )
( format t "3. Output guests who have paid more than input~%" )
( format t "4. Remove guest~%" )
( format t "5. Exit~%" )
( format t "~%Enter choice: " )
( setq choice ( read ) )
( setq _guests '() )
( cond
( ( eq choice 5 ) ( return ) )
;;;; this part doesn't work as desired
( ( eq choice 1 )
( if ( null _guests )
( setq _guests ( cons ( input_person ) _guests ) )
( setq _guests ( append _guests ( input_person ) ) ) ) )
;;;;
( ( eq choice 2 )
( dolist ( n _guests )
( princ n ) ) ) ) ) )
Of course the extra spacing around each paren is also unusual, but it's less of a problem. The double line spacing you were using really made your code unreadable.

Related

Update table with IF statement in Postgresql

How can I do such query in PostgreSQL?
update table1 set column9 =
concat(
(if (b-a from table1)>1 then b-a else 0),',',
(if (c-b from table1)>1 then c-b else 0),',',
(if (d-c from table1)>1 then d-c else 0)
)
Use CASE expressions like this:
UPDATE table1
SET column9 = CONCAT(
CASE WHEN b-a > 1 then b-a ELSE 0 END, ',',
CASE WHEN c-b > 1 then c-b ELSE 0 END, ',',
CASE WHEN d-c > 1 then d-c ELSE 0 END
);
See a simplified demo.

REGEX for special character (Decode Unicode)

I have try this code to decode, unicode all works fine if the comment start with any special character, symbol, space and etc shows error.
CREATE TEMP FUNCTION DecodeUnicode(s STRING) AS (
IF(s NOT LIKE '%\\u%', s,
(SELECT CODE_POINTS_TO_STRING(ARRAY_AGG(CAST(CONCAT('0x', x) AS INT64)))
FROM UNNEST(SPLIT(s, '\\u')) AS x
WHERE x != ''))
);
SELECT
original,
DecodeUnicode(original) AS decoded
FROM (
SELECT trim(r'$-\u6599\u91d1\u304c\u9ad8\u3059\u304e\uff01\uff01\uff01') AS original UNION ALL
SELECT trim(r'abcd')
);
Below is for BigQuery Standard SQL
#standardSQL
CREATE TEMP FUNCTION DecodeUnicode(s STRING) AS (
(SELECT CODE_POINTS_TO_STRING(ARRAY_AGG(CAST(CONCAT('0x', x) AS INT64)))
FROM UNNEST(SPLIT(s, '\\u')) AS x
WHERE x != ''
)
);
WITH `yourTable` AS (
SELECT r'$-\u6599\u91d1\u304c\u9ad8\u3059\u304e\uff01\uff01\uff01' AS original UNION ALL
SELECT r'abcd'
), uchars AS (
SELECT DISTINCT
c,
DecodeUnicode(c) uchar
FROM `yourTable`,
UNNEST(REGEXP_EXTRACT_ALL(original, r'(\\u[abcdef0-9]{4})')) c
)
SELECT
original,
STRING_AGG(IFNULL(uchar, x), '' ORDER BY pos) decoded
FROM (
SELECT
original,
pos,
SUBSTR(original,
SUM(CASE char WHEN '' THEN 1 ELSE 6 END)
OVER(PARTITION BY original ORDER BY pos) - CASE char WHEN '' THEN 0 ELSE 5 END,
CASE char WHEN '' THEN 1 ELSE 6 END) x,
uchar
FROM `yourTable`,
UNNEST(REGEXP_EXTRACT_ALL(original, r'(\\u[abcdef0-9]{4})|.')) char WITH OFFSET AS pos
LEFT JOIN uchars u ON u.c = char
)
GROUP BY original
-- ORDER BY original
What it does - it extracts all unicode characters and decode them and replace them in original string leaving non-unicodes stay as they are, so the output will be as below
original decoded
$-\u6599\u91d1\u304c\u9ad8\u3059\u304e\uff01\uff01\uff01 $-料金が高すぎ!!!
abcd abcd

Need to go from hostname to base domain

I need a function:
f(fqdn,suffix) -> basedomain
with these example inputs and outputs:
f('foobar.quux.somedomain.com','com') -> 'somedomain.com'
f('somedomain.com','com') -> 'somedomain.com'
f('foobar.quux.somedomain.com.br','com.br') -> 'somedomain.com.br'
f('somedomain.com.br','com.br') -> 'somedomain.com.br'
In plain English, if the suffix has n segments, take the last n+1 segments. Find the base domain for the FQDN, allowing for the fact that some FQDNs have more than one suffix element.
The suffixes I need to match are here. I've already got them in my SQL database.
I could write this in C#; it might not be the most elegant but it would work. Unfortunately I would like to have this function in either T-SQL, where it is closest to the data, or in Powershell, which is where the rest of the utility that consumes this data is going to be. I suppose it would be ok to do it in C#, compile to an assembly and then access it from T-SQL, or even from Powershell ... if that would be the fastest executing. If there's some reasonably clever alternative in pure T-SQL or simple Powershell, I'd like that.
EDIT: One thing I forgot to mention explicitly (but which is clear when reviewing the suffix list, at my link above) is that we must pick the longest matching suffix. Both "br" and "com.br" appear in the suffix list (with similar things happening for uk, pt, etc). So the SQL has to use a window function to make sure the longest matching suffix is found.
Here is how far I got when I was doing the SQL. I had gotten lost in all the substring/reverse functions.
SELECT Domain, suffix
FROM (
SELECT SD.Domain, SL.suffix,
RN=ROW_NUMBER() OVER (
PARTITION BY sd.Domain ORDER BY LEN(SL.suffix) DESC)
FROM SiteDomains SD
INNER JOIN suffixlist SL ON SD.Domain LIKE '%.'+SL.suffix
) AS X
WHERE RN=1
This works ok for finding the right suffix. I'm a little concerned about its performance though.
The following demonstrates matching FQDNs with TLDs and extracting the desired n + 1 domain name segments:
-- Sample data.
declare #SampleTLDs as Table ( TLD VarChar(64) );
insert into #SampleTLDs ( TLD ) values
( 'com' ), ( 'somedomain.com' ), ( 'com.br' );
declare #SampleFQDNs as Table ( FQDN VarChar(64) );
insert into #SampleFQDNs ( FQDN ) values
( 'foobar.quux.somedomain.com' ), ( 'somedomain.com' ),
( 'foobar.quux.somedomain.com.br' ), ( 'somedomain.com.br' );
select * from #SampleTLDs;
select * from #SampleFQDNs;
-- Fiddle about.
select FQDN, TLD,
case
when DotPosition = 0 then FQDN
else Reverse( Left( ReversedPrefix, DotPosition - 1) ) + '.' + TLD
end as Result
from (
select FQDNs.FQDN, TLDs.TLD,
Substring( Reverse( FQDNs.FQDN ), Len( TLDs.TLD ) + 2, 100 ) as ReversedPrefix,
CharIndex( '.', Substring( Reverse( FQDNs.FQDN ), Len( TLDs.TLD ) + 2, 100 ) ) as DotPosition
from #SampleFQDNs as FQDNs inner join
#SampleTLDs as TLDs on FQDNs.FQDN like '%.' + TLDs.TLD or FQDNs.FQDN = TLDs.TLD ) as Edna;
-- To select only the longest matching TLD for each FQDN:
with
ExtendedFQDNs as (
select FQDNs.FQDN, TLDs.TLD, Row_Number() over ( partition by FQDN order by Len( TLDs.TLD ) desc ) as TLDLenRank,
Substring( Reverse( FQDNs.FQDN ), Len( TLDs.TLD ) + 2, 100 ) as ReversedPrefix,
CharIndex( '.', Substring( Reverse( FQDNs.FQDN ), Len( TLDs.TLD ) + 2, 100 ) ) as DotPosition
from #SampleFQDNs as FQDNs inner join
#SampleTLDs as TLDs on FQDNs.FQDN like '%.' + TLDs.TLD or FQDNs.FQDN = TLDs.TLD )
select FQDN, TLD,
case
when DotPosition = 0 then FQDN
else Reverse( Left( ReversedPrefix, DotPosition - 1) ) + '.' + TLD
end as Result
from ExtendedFQDNs
where TLDLenRank = 1;
Here's how I would do it in C#:
string getBaseDomain(string fqdn, string suffix)
{
string[] domainSegs = fqdn.Split('.');
return domainSegs[domainSegs.Length - suffix.Split('.').Length - 1] + "." + suffix;
}
So here it is in Powershell:
function getBaseDomain
{
Param(
[string]$fqdn,
[string]$suffix
)
$domainSegs = $fqdn.Split(".");
return $domainSegs[$domainSegs.Length - $suffix.Split(".").Length - 1] + "."+$suffix;
}
Seems rather silly now to have wasted stackoverflow.com's time with this. My apologies.
Here is a tsql variant...
declare #fqdn varchar(256) = 'somedomain.com'
declare #suffix varchar(128) = 'com'
select left(#fqdn,CHARINDEX(#suffix,#fqdn) - 2)
if(select CHARINDEX('.',reverse(left(#fqdn,CHARINDEX(#suffix,#fqdn) - 2)))) = 0
begin
select left(#fqdn,CHARINDEX(#suffix,#fqdn) - 2) + '.' + #suffix
end
else
begin
select right(left(#fqdn,CHARINDEX(#suffix,#fqdn) - 2),CHARINDEX('.',reverse(left(#fqdn,CHARINDEX(#suffix,#fqdn) - 2))) - 1) + '.' + #suffix
end

Sort order of search results in Postgres SQL

I have following data available in PostgreSQL in same sequence-
Building Maintenance - Painting
Paint Expense
Painting Supplies
Search result should display in following order:
Exact matches should display first
Partial matches from the beginning of the text string
Partial matches anywhere in the text string**
If user enters search string "paint"
Result should be like -
Paint Expense
Painting Supplies
Building Maintenance - Painting
Any help would be appreciated.
This is for SQL Server but you will simply change this to PostrgeSQL:
DECLARE #f VARCHAR(MAX) = 'paint'
DECLARE #t TABLE ( v VARCHAR(MAX) )
INSERT INTO #t
VALUES ( 'Building Maintenance - Painting' ),
( 'Paint Expense' ),
( 'Painting Supplies' )
SELECT * ,
CASE WHEN ( v LIKE #f + ' %'
OR v LIKE '% ' + #f
OR v LIKE '% ' + #f + ' %'
) THEN 1
WHEN v LIKE #f + '%' THEN 2
WHEN v LIKE '%' + #f + '%' THEN 3
ELSE 4
END AS ordering
FROM #t
ORDER BY ordering
Output:
v ordering
Paint Expense 1
Painting Supplies 2
Building Maintenance - Painting 3
The first is exact match like 'paint ...' or '... paint' or '... paint ...'.
The second: 'paint...'.
The third: '...paint...'.
You can also do this directly in order clause:
SELECT *
FROM #t
ORDER BY CASE WHEN ( v LIKE #f + ' %'
OR v LIKE '% ' + #f
OR v LIKE '% ' + #f + ' %'
) THEN 1
WHEN v LIKE #f + '%' THEN 2
WHEN v LIKE '%' + #f + '%' THEN 3
ELSE 4
END

rearrage lines of file in unix

I have a file with the below format. This is a snippet, original lines cross over a million.
Infact ABC/DEF/GH9j/etc.. is the starting of a line and ";" is supposed to be the end of the line.
Here in this example line01, line10 & line13 are perfect; But other lines are scattered in to multiple lines:
line01: ABC abc_123 ( .Y ( B2b ) , .A ( sel ) );
line02: DEF def_456 ( .Z ( n_2 ) , .in ( 1b0 ) ,
line03: .tstin ( sel ) , .tstmb ( DD ) );
line04: GH9j 3_inst ( .Q0 ( CC3 ) , .Q1 ( Ee ) ,
line05: .Q2 ( p_2 ) , .Q3 ( cin ) ,
line06: .D0 ( AA ) , .D1 ( rdata[5] ) ,
line07: .D2 ( gg ) , .D3 ( hp ) ,
line08: .SE0 ( sel ) , .SE1 ( sel ) ,
line09: .SE2 ( pqr ) , .SE3 ( AA ) , .CK ( Bb ) );
line10: BUF 4PQR ( .Y ( eE ) , .A ( cC ) );
line11: MX2 MnOp ( .X ( DD ) , .A ( PQR_11 ) ,
line12: .B ( trstb ) , .S0 ( klm2 ) );
line13: BUFH 6th_inst ( .Zz ( AA ) , .A ( B2B ) );
.......
Q. I want to rearrange all lines like below:
All the ports of one instance should be in ONE LINE (13 lines should reduce to 6 lines)
line01: ABC abc_123 ( .Y ( B2b ) , .A ( sel ) );
line02: DEF def_456 ( .Z ( n_2 ) , .in ( 1b0 ) , .tstin ( sel ) , .tstmb ( DD ) );
line03: GH9j 3_inst ( .Q0 ( CC3 ) , .Q1 ( Ee ) , .Q2 ( p_2 ) , .Q3 ( cin ) , .D0 ( AA ) , .D1 ( rdata[5] ) , .D2 ( gg ) , .D3 ( hp ) , .SE0 ( sel ) , .SE1 ( sel ) , .SE2 ( pqr ) , .SE3 ( AA ) , .CK ( Bb ) );
line04: BUF 4PQR ( .Y ( eE ) , .A ( cC ) );
line05: MX2 MnOp ( .X ( DD ) , .A ( PQR_11 ) , .B ( trstb ) , .S0 ( klm2 ) );
line06: BUFH 6th_inst ( .Zz ( AA ) , .A ( B2B ) );
A one liner might work: perl -pe' chomp unless m/\;/' file.txt - though with millions of lines some tweaking might be needed.
Here is the one liner as a script:
#!/usr/bin/env perl
while (<DATA>) {
chomp unless m/\;/ ;
print ;
}
__DATA__
ABC abc_123 ( .Y ( B2b ) , .A ( sel ) );
DEF def_456 ( .Z ( n_2 ) , .in ( 1b0 ) ,
.tstin ( sel ) , .tstmb ( DD ) );
GH9j 3_inst ( .Q0 ( CC3 ) , .Q1 ( Ee ) ,
.Q2 ( p_2 ) , .Q3 ( cin ) ,
.D0 ( AA ) , .D1 ( rdata[5] ) ,
.D2 ( gg ) , .D3 ( hp ) ,
.SE0 ( sel ) , .SE1 ( sel ) ,
.SE2 ( pqr ) , .SE3 ( AA ) , .CK ( Bb ) );
BUF 4PQR ( .Y ( eE ) , .A ( cC ) );
MX2 MnOp ( .X ( DD ) , .A ( PQR_11 ) ,
.B ( trstb ) , .S0 ( klm2 ) );
BUFH 6th_inst ( .Zz ( AA ) , .A ( B2B ) );
Output:
ABC abc_123 ( .Y ( B2b ) , .A ( sel ) );
DEF def_456 ( .Z ( n_2 ) , .in ( 1b0 ) ,.tstin ( sel ) , .tstmb ( DD ) );
GH9j 3_inst ( .Q0 ( CC3 ) , .Q1 ( Ee ) ,.Q2 ( p_2 ) , .Q3 ( cin ) ,.D0 ( AA ) , .D1 ( rdata[5] ) ,.D2 ( gg ) , .D3 ( hp ) ,.SE0 ( sel ) , .SE1 ( sel ) ,.SE2 ( pqr ) , .SE3 ( AA ) , .CK ( Bb ) );
BUF 4PQR ( .Y ( eE ) , .A ( cC ) );
MX2 MnOp ( .X ( DD ) , .A ( PQR_11 ) ,.B ( trstb ) , .S0 ( klm2 ) );
BUFH 6th_inst ( .Zz ( AA ) , .A ( B2B ) );
If you need to preserve the line numbers (line01:) add a note to that effect in the question.
Here is a sed solution:
sed -n 'H;/;/{s/.*//;x;s/\n//g;p;}' filename
Without seeing your whole dataset, or at least large chuncks of it, its hard to know what pattern to match on, however in the example you give, it seems as though the lines you want to merge end in a ,newline therefore simply changing ,newline to , might do the trick:
sed -e ':begin;$!N;s/,\n/,/;tbegin;P;D' in.txt
outputs:
ABC abc_123 ( .Y ( B2b) , .A ( sel));
DEF def_456 ( .Z ( n_2) , .in ( 1b0) ,.tstin ( sel) , .tstmb ( DD));
GH9j 3_inst ( .Q0 ( CC3) , .Q1 ( Ee) ,.Q2 ( p_2) , .Q3 ( cin) ,.D0 ( AA) , .D1 ( rdata [5]) ,.D2 ( gg) , .D3 ( hp) ,.SE0 ( sel) , .SE1 ( sel) ,.SE2 ( pqr) , .SE3 ( AA) , .CK ( Bb));
BUF 4PQR ( .Y ( eE) , .A ( cC));
MX2 MnOp ( .X ( DD) , .A ( PQR_11) ,.B ( trstb) , .S0 ( klm2));
BUFH 6th_inst ( .Zz ( AA) , .A ( B2B));
See http://backreference.org/2009/12/23/how-to-match-newlines-in-sed/ for more details on matching newlines in sed.
A one-liner in AWK
awk '{printf "%s%s", $0, /;\s*$/ ? "\n" : " "}' filename
It outputs each line with a separator that is either a newline or space depending on whether ; is found at the end of the line.