1 | ||
1 | ||
1 | ||
1 | ||
1 |
I wanted to keep my last post on this idea short and to the point. Essentially I wanted to keep it to why it's useful and the code someone would need. If you want to read that version you can find it here:
https://goatmatrix.net/c/Programming/7CcPALRiSA
In this post I want to add all the extra stuff I wanted share. There are really two parts to that.
First is I realize that Nim is not the most widely know language so I also have a version in NodeJS that perhaps will be legible to an additional set of people.
The next part is the far more nerdy way to do it that would have been a distraction to a simple post sharing a utility.
Might as well get the Javascript version out of the way now.
#!/usr/bin/env node
function multiplytext(a,b) {
if(!b) { //We are a core list;
return a.reduce(multiplytext);
}
var out=[];
for(var atext of a) {
for(var btext of b) {
out.push(`${atext} ${btext}`);
}
}
return out;
}
var readline = require('readline'); // createInterface: read STDIN line-by-line :contentReference[oaicite:0]{index=0}
var rl = readline.createInterface({input: process.stdin}); // no output stream needed for plain reads :contentReference[oaicite:1]{index=1}
var groups = [], current = []; // groups: array of arrays; current: lines in the current group
rl.on('line', line => {
line = line.trim(); // drop whitespace :contentReference[oaicite:2]{index=2}
if (line === '') { // empty line ⇒ end of a group
if (current.length) groups.push(current); // only push non-empty groups
current = []; // reset for next group
} else {
current.push(line); // accumulate lines into current group
}
});
rl.on('close', () => {
if (current.length) groups.push(current); // push the last group if no trailing blank line
for (var outline of multiplytext(groups)) // for-of + template strings :contentReference[oaicite:3]{index=3}
console.log(outline);
});
Ok. Now onto the even nerdier way to do it for the nerdy sake. I wanted to write it in Erlang since that is a language I am learning. I got it done only to find it has a 0.2s delay. This is small and really caused by initializing the BEAM VM Erlang depends on. It's small but doesn't feel perfect especially for small amounts of text. This is unacceptable. Let's turn this into an Erlang based microservice over TCP so we don't have initialization time. Just in case you need it to be fast and highly available. IDK, maybe your text multiplication needs to scale.
First things first install Erlang and a few other things
pacman -Syu
pacman -S erlang rebar3 netcat
Now start a new erlang project
rebar3 new app multserver
cd multserver/src
Now we are going to modify multserver_app.erl
-module(multserver_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_Type, _Args) ->
Port = case application:get_env(multserver, port) of {ok,P}->P; _->5050 end,
Pid = spawn_link(fun() -> multserver:listen(Port) end),
{ok, Pid}.
stop(_State) -> ok.
And the last file to create is our actual server, multserver.erl
-module(multserver).
-export([listen/1]).
listen(Port) ->
{ok, L} = gen_tcp:listen(Port, [binary,{packet,line},{active,false},{reuseaddr,true},{nodelay,true}]),
accept(L).
accept(L) ->
{ok, S} = gen_tcp:accept(L),
spawn(fun() -> handle(S) end),
accept(L).
handle(S) ->
Lines = recv_all(S),
Groups = lines_to_groups(Lines),
Outs = multiply(Groups),
send_all(Outs,S),
gen_tcp:close(S).
send_all([],S) -> ok;
send_all([H|T],S) ->
gen_tcp:send(S,H),
gen_tcp:send(S,"\n"),
send_all(T,S).
recv_all(S) ->
case gen_tcp:recv(S, 0) of
{ok, Bin} ->
Len = byte_size(Bin),
Str = binary_to_list(binary:part(Bin,0,Len-1)), % Remove trailing \n
case Str of
[27] -> []; % Escape signals end
_ -> [Str|recv_all(S)]
end;
{error,closed} -> []
end.
% Group lines seperated by an empty line
% Works for both lists and binary
lines_to_groups([[]|T]) -> % End of group for list
[[]|lines_to_groups(T)];
lines_to_groups([<<>>|T]) -> % End of group for binary
[[]|lines_to_groups(T)];
lines_to_groups([H]) -> [[H]];
lines_to_groups([H|T])->
[L|G]=lines_to_groups(T),
[[H|L]|G].
% Perform cartician multiple between all lists using concatenation as the base operation
multiply([]) -> []; % An empty list remains empty
multiply([H]) -> H; % Multiplication with only one operand is itself
multiply([H,T]) -> [X ++ " " ++ Y || X <- H, Y <- T]; % Cartisian multiply exactly two lists
multiply([H1,H2|Rest]) -> % Multiply the tail first because ++ performs better with a short first operand
multiply([H1, multiply([H2|Rest])]).
Add a wrapper script to your ~/bin/mult
#!/bin/bash
{ cat; printf "\033\n"; } | netcat 127.0.0.1 5050
I'm not going to go over configuring or running a release so we'll just use rebar3 shell. cd back to the root of the project root directory and run rebar3.
cd ..
rebar3 shell
The server is running. Open vim and enter a few lines.
Hello
Hi
Ahoy
World
Bye
Hoy
Highlight all the text you want to expand into variants with shift-V. Then run :!mult
You've now expanded your text using the infinitely scalable BEAM VM and the highest paying programming language according to StackOverflow.