在Erlang中连续提取元组列表中的值

时间:2014-12-04 23:44:33

标签: erlang

我正在从Ruby背景学习Erlang,并且在掌握思维过程时遇到了一些困难。我试图解决的问题如下:

我需要向api发出相同的请求,每次我在响应中收到一个唯一的ID,我需要传递给下一个请求,直到没有返回ID。从每个响应中我需要提取某些数据并将其用于其他事情。

首先获取迭代器:

ShardIteratorResponse = kinetic:get_shard_iterator(GetShardIteratorPayload).
{ok,[{<<"ShardIterator">>,
      <<"AAAAAAAAAAGU+v0fDvpmu/02z5Q5OJZhPo/tU7fjftFF/H9M7J9niRJB8MIZiB9E1ntZGL90dIj3TW6MUWMUX67NEj4GO89D"...>>}]}

解析shard_iterator ..

{_, [{_, ShardIterator}]} = ShardIteratorResponse.

向溪流记录发出kinesis请求......

GetRecordsPayload = [{<<"ShardIterator">>, <<ShardIterator/binary>>}].
[{<<"ShardIterator">>,
  <<"AAAAAAAAAAGU+v0fDvpmu/02z5Q5OJZhPo/tU7fjftFF/H9M7J9niRJB8MIZiB9E1ntZGL90dIj3TW6MUWMUX67NEj4GO89DETABlwVV"...>>}]
14> RecordsResponse = kinetic:get_records(GetRecordsPayload).
{ok,[{<<"NextShardIterator">>,
      <<"AAAAAAAAAAFy3dnTJYkWr3gq0CGo3hkj1t47ccUS10f5nADQXWkBZaJvVgTMcY+nZ9p4AZCdUYVmr3dmygWjcMdugHLQEg6x"...>>},
     {<<"Records">>,
      [{[{<<"Data">>,<<"Zmlyc3QgcmVjb3JkISEh">>},
         {<<"PartitionKey">>,<<"BlanePartitionKey">>},
         {<<"SequenceNumber">>,
          <<"49545722516689138064543799042897648239478878787235479554">>}]}]}]}

我正在努力的是如何编写一个循环来持续点击该流的kinesis端点,直到没有更多的碎片迭代器,我想要所有记录。因为我不能像在Ruby中那样重新分配变量。

2 个答案:

答案 0 :(得分:1)

警告:我的代码可能会被窃听,但是它会关闭&#34;。我从未运行它,也不知道最后的迭代器是什么样的。

我看到你正试图完全用shell做你的工作。它可能但很难。您可以使用命名函数和递归(since release 17.0 it's easier),例如:

F = fun (ShardIteratorPayload) ->
    {_, [{_, ShardIterator}]} = kinetic:get_shard_iterator(ShardIteratorPayload),
    FunLoop =
        fun Loop(<<>>, Accumulator) ->  % no clue how last iterator can look like
                lists:reverse(Accumulator);
            Loop(ShardIterator, Accumulator) ->
                {ok, [{_, NextShardIterator}, {<<"Records">>, Records}]} =
                    kinetic:get_records([{<<"ShardIterator">>, <<ShardIterator/binary>>}]),
                Loop(NextShardIterator, [Records | Accumulator])
        end,
    FunLoop(ShardIterator, [])
end.
AllRecords = F(GetShardIteratorPayload).

但输入shell太复杂了......

在模块中编写代码要容易得多。 erlang中的一个常见模式是生成另一个进程或进程来获取数据。为了简单起见,您可以通过调用spawn or spawn_link来生成其他流程,但现在不要使用链接,只使用spawn/3。 让我们编译简单的消费者模块:

-module(kinetic_simple_consumer).

-export([start/1]).

start(GetShardIteratorPayload) ->
    Pid = spawn(kinetic_simple_fetcher, start, [self(), GetShardIteratorPayload]),
    consumer_loop(Pid).

consumer_loop(FetcherPid) ->
    receive
        {FetcherPid, finished} ->
            ok;
        {FetcherPid, {records, Records}} ->
            consume(Records),
            consumer_loop(FetcherPid);
        UnexpectedMsg -> 
            io:format("DROPPING:~n~p~n", [UnexpectedMsg]),
            consumer_loop(FetcherPid)
    end.

consume(Records) ->
    io:format("RECEIVED:~n~p~n",[Records]).

和抓手:

-module(kinetic_simple_fetcher).

-export([start/2]).

start(ConsumerPid, GetShardIteratorPayload) ->
    {ok, [ShardIterator]} = kinetic:get_shard_iterator(GetShardIteratorPayload),
    fetcher_loop(ConsumerPid, ShardIterator).

fetcher_loop(ConsumerPid, {_, <<>>}) -> % no clue how last iterator can look like
    ConsumerPid ! {self(), finished};

fetcher_loop(ConsumerPid, ShardIterator) ->
    {ok, [NextShardIterator, {<<"Records">>, Records}]} = 
        kinetic:get_records(shard_iterator(ShardIterator)),
    ConsumerPid ! {self(), {records, Records}},
    fetcher_loop(ConsumerPid, NextShardIterator).

shard_iterator({_, ShardIterator}) ->
    [{<<"ShardIterator">>, <<ShardIterator/binary>>}].

正如您所看到的,两个流程可以同时完成工作。 试试你的shell:

kinetic_simple_consumer:start(GetShardIteratorPayload).

现在您看到您的shell进程转向使用者,并且在fetcher发送{ItsPid, finished}之后您将返回shell。

下次而不是

kinetic_simple_consumer:start(GetShardIteratorPayload).

运行:

spawn(kinetic_simple_consumer, start, [GetShardIteratorPayload]).

你应该玩产卵过程 - 它是erlang的主要力量。

答案 1 :(得分:0)

在Erlang中,您可以使用尾递归函数编写循环。我不知道动态API,所以为了简单起见,我假设kinetic:next_iterator/1在没有更多分片时返回{ok, NextIterator}{error, Reason}

loop({error, Reason}) ->
    ok;
loop({ok, Iterator}) ->
    do_something_with(Iterator),
    Result = kinetic:next_iterator(Iterator),
    loop(Result).

您正在用迭代替换循环。第一个子句处理的情况是,没有更多的分片(总是以结束条件开始递归)。第二个子句处理case,我们得到了一些迭代器,我们用它做一些事情然后调用它。

递归调用是函数体中的最后一条指令,称为尾递归。 Erlang优化了这样的调用 - 它们不使用调用堆栈,因此它们可以在常量内存中无限运行(你不会得到类似“堆栈级别太深”的内容)