unmatched has asked for the wisdom of the Perl Monks concerning the following question:
Hi Monks,
Today I decided to practice some Perl by porting a Python script. What took me the most to wrap my head around was working with JSON data using JSON::XS::decode_json. In my example below, this returns an array of hashes, i.e.: [{}, {}, {}]. Therefore, I would've thought that the return value could be inferred as a list, and declared using:
@decoded = decode_json($result);
But this didn't work as expected, and this threw me off when trying to do something simple like looping on the resulting value. I found a few similar questions online and managed to finish the script, but I was wondering if there's a better way of doing this. In particular, the syntax for my $workspace (@$decoded) seems really weird to me. Do you have any thoughts on this?
For context, I'm using the i3 window manager and I often want to move windows over to the next empty workspace. This problems translates to finding the next non-consecutive number in a sequence of numbers. For example, given the workspaces 1, 2, 3, 5 and 6, while currently focusing workspace 1, this returns workspace #4. If I'm focused on workspace #5, however, it would create a new workspace at #7.
#!/bin/perl use v5.10; use strict; use warnings; use JSON::XS qw/decode_json/; use feature qw/say/; sub main { my $result = qx{ i3-msg -t get_workspaces }; # returns [{},{},{}] my $decoded = decode_json($result); my $current = 0; for my $workspace (@$decoded) { my $num = $workspace->{'num'}; if ($workspace->{'focused'}) { $current = $num; next; } # Ignore workspaces below the currently focused one. if ($current == 0) { next; } # Found the gap! if ($current + 1 != $num) { last; } # Keep increasing the soon-to-be current workspace. $current = $num; } say $current + 1; } main();
And the Python version, in case you want to give some advice on this too! :P
#!/bin/python3 import json import subprocess def main(): result = subprocess.run( ['i3-msg', '-t', 'get_workspaces'], capture_output=True, check=True ) i3_workspaces = json.loads(result.stdout) current_workspace = next((x['num'] for x in i3_workspaces if x['fo +cused'])) last_workspace = i3_workspaces[-1]['num'] remaining_workspaces = (x['num'] for x in i3_workspaces if x['num' +] >= current_workspace) for i in range(current_workspace, last_workspace): if i != next(remaining_workspaces): print(i) return print(last_workspace + 1) if __name__ == '__main__': main()
And for some fun, I decided to compare the performance of each. I know it's a really simple script and the test is basically meaningless, but I was curious, and actually the results are surprising to me:
time $(for i in {1..100}; do ./move_to_next.pl> /dev/null; done;) Perl: real 0m1.113s user 0m0.843s sys 0m0.266s Python: real 0m1.852s user 0m1.516s sys 0m0.323s
But then I changed the code to run the loop inside the program itself, basically calling `main` 100 times, and running the test again like so:
time ./move_to_next.pl > /dev/null Perl: real 0m0.231s user 0m0.116s sys 0m0.116s Python: real 0m0.099s user 0m0.078s sys 0m0.018s
So, it seems that spawning the Python interpreter itself is quite costly, but it runs faster on the long run? I also saw a video about how the JSON module in Perl is quite slow, could that be it?
Anyway, thank you in advance!
|
|---|