You don't need to store the column to reference it that way. Try this:
To set up:
CREATE TABLE tbl
(zipcode text NOT NULL, city text NOT NULL, state text NOT NULL);
INSERT INTO tbl VALUES ('10954', 'Nanuet', 'NY');
We can see we have "the right stuff":
\pset border 2
SELECT * FROM tbl;
+---------+--------+-------+ | zipcode | city | state | +---------+--------+-------+ | 10954 | Nanuet | NY | +---------+--------+-------+
Now add a function with the desired "column name" which takes the record type of the table as its only parameter:
CREATE FUNCTION combined(rec tbl)
RETURNS text
LANGUAGE SQL
AS $$
SELECT $1.zipcode || ' - ' || $1.city || ', ' || $1.state;
$$;
This creates a function which can be used as if it were a column of the table, as long as the table name or alias is specified, like this:
SELECT *, tbl.combined FROM tbl;
Which displays like this:
+---------+--------+-------+--------------------+ | zipcode | city | state | combined | +---------+--------+-------+--------------------+ | 10954 | Nanuet | NY | 10954 - Nanuet, NY | +---------+--------+-------+--------------------+
This works because PostgreSQL checks first for an actual column, but if one is not found, and the identifier is qualified with a relation name or alias, it looks for a function like the above, and runs it with the row as its argument, returning the result as if it were a column. You can even index on such a "generated column" if you want to do so.
Because you're not using extra space in each row for the duplicated data, or firing triggers on all inserts and updates, this can often be faster than the alternatives.