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!
In reply to Adjusting variable context when workign with JSON data? by unmatched
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |