X -> (butlast-h X []))
(define butlast-h
{(list A) --> (list A) --> (list A)}
[_] B -> B
[X | Y] B -> (butlast-h Y (append B [X])))
The generalisation of butlast with an extra parameter is often known as the "drop" function, i.e (BUTLAST {1 2 3 4 5] 3) is [1 2]
meaning, you drop the last 3 items.
There is also a function (not in CL), known as "take" where (take X K) takes the first K elements of X, and hence drops the last N - K elements,
where N is the length of the X.
Here is a (tail-recursive) Shen implementation :
(define take
{(list A) --> number --> (list A)}
_ K -> [] where (<= K 0)
X K -> (take-h X K []))
(define take-h
{(list A) --> number --> (list A) --> (list A)}
[] _ B -> B
X 0 B -> B
[X | Y] K B -> (take-h Y (- K 1) (append B [X])))
The drop function becomes
(define drop
{(list A) --> number --> (list A)}
X K -> (take X (- (length X) K)))
I do not know if it is possible to implement "drop" without determining the length of the list, or using "reverse" twice.