• 高阶Erlang:超大只的问答房间


    使用gen_server实现的更为复杂的。。。山寨kahoot!!!!!!

    然鹅这次我们换名字了,现在它叫阿童木TA(robota)!

    -module(robota).
    -behaviour(gen_server).
    
    %% Test helper function
    -export([get_module_grader/1, get_concurrent_grader/1, valid/3,
            get_mgrader/1, run_all_module_grader/1, troels_eval/2, check/2, eval/3]).
    
    
    %% API
    -export([get_the_show_started/0, new/2,
             add_part/3, del_part/2, available/1, unavailable/1, status/1,
             grade/4]).
    
    %% gen_server callbacks
    -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
             terminate/2, code_change/3]).
    
    -define(SERVER, ?MODULE).
    
    %%%===================================================================
    %%% API
    %%%===================================================================
    
    get_the_show_started() ->
        try 
            gen_server:start_link({local, ?SERVER}, ?MODULE, [], [])
        catch 
            _ : _ -> {error,"Failed to crate a RoboTA!/n"}
        end.
    
    new(RoboTA, Name) ->
       gen_server:call(RoboTA, {new, Name}).
    
    add_part(AssHandler, Label, Grader) ->
        gen_server:call(?MODULE, {add_part, AssHandler, Label, Grader}).
    
    del_part(AssHandler, Label) ->
        gen_server:cast(?MODULE, {del_part, AssHandler, Label}).
    
    available(AssHandler) ->
        gen_server:call(?MODULE, {available, AssHandler}).
    
    unavailable(AssHandler) ->
        gen_server:call(?MODULE, {unavailable, AssHandler}).
    
    status(AssHandler) ->
         gen_server:call(?MODULE, {status, AssHandler}).
    
    grade(RoboTA, Name, Submission, Pid) ->
        gen_server:call(RoboTA, {grade, Name, Submission, Pid}).
    
    
    %%%===================================================================
    %%% gen_server callbacks
    %%%===================================================================
    init([]) -> {ok, #{}}.
    
    
    handle_call({new, Name}, _From, State) -> 
        case maps:find(Name, State) of 
            {ok, _} -> {reply, {error,the_name_has_been_token}, State};
            _ -> Ref = make_ref(),
                 AssHandler = {Name, Ref},
                 NewState = maps:put(Name, {[], false, []}, State),
                 {reply, {ok, AssHandler}, NewState}
        end;
       
    handle_call({add_part, AssHandler, Label, Grader}, _From, State) -> 
        {Name, _Ref} = AssHandler,
        case maps:find(Name, State) of 
            % check assignment not active
            {ok, {AssignmentList,false, GraderList}} -> 
                % check label unique
                case lists:keymember(Label, 1, AssignmentList) of
                    true -> {reply, {error, {Label, not_unique}}, State};
                    false ->
                            NewAssignmentList = lists:append(AssignmentList,[{Label,Grader}]),
                            NewState = maps:put(Name, {NewAssignmentList, false, GraderList}, State),
                            {reply, ok, NewState}
                end;
            {ok, {_AssignmentList,true, _}} ->
                {reply, {error, {Name, is_available}}, State};
            _ -> {reply, {error, fake_asshandler}, State}
        end;
    
    
        
    handle_call({available, AssHandler}, _From, State) -> 
        {Name, _Ref} = AssHandler,
        case maps:find(Name, State) of 
            {ok, {AssignmentList,false, GraderList}} -> 
                ModuleGrader = get_mgrader(AssignmentList),
                case ModuleGrader of
                    [] -> NewState = maps:put(Name, {AssignmentList, true}, State),
                          {reply, ok, NewState};
                    _ -> 
                        try 
                            NewAssignmentList = run_all_module_grader(AssignmentList),
                            NewState = maps:put(Name, {NewAssignmentList, true, GraderList}, State),
                            {reply, ok, NewState}
                        catch
                            Error -> Error
                        end                   
                end;           
            {ok, {_AssignmentList,true, _}} ->
                {reply, {error, already_available}, State};        
            _ -> {reply, {error, fake_asshandler}, State}     
        end;
    
    handle_call({unavailable, AssHandler}, _From, State) -> 
        {Name, _Ref} = AssHandler,
        case maps:find(Name, State) of 
            {ok, {AssignmentList, true, GraderList}} -> 
                % check no submission under grading and all graders have been upload
                ModuleGrader = get_mgrader(AssignmentList),
                try
                    unload_all(ModuleGrader),
                    check_no_grading(GraderList),
                    NewState = maps:put(Name, {AssignmentList, false, []}, State),
                    {reply, ok, NewState}
                catch
                    Reason -> {reply, {error, Reason}, State}
                end;           
            {ok, {_AssignmentList,false, _}} ->
                {reply, {error, already_unavailable}, State};        
            _ -> {reply, {error, fake_asshandler}, State}     
        end;
    
    handle_call({status, AssHandler}, _From, State) -> 
        {Name, _Ref} = AssHandler,
        case maps:find(Name, State) of 
            {ok, {AssignmentList, false, _}} -> 
                {reply, {unavailable, AssignmentList}, State};
            {ok, {AssignmentList, true, _}} ->
                {reply, {available, AssignmentList}, State}     
        end;
    
    
    
    handle_call({grade, Name, Submission, Pid}, _From, State) -> 
        case maps:find(Name, State) of 
            {ok, {_AssignmentList, false, _}} -> 
                {reply, {error, {Name, is_unavailable}}, State};
            {ok, {AssignmentList, true, GraderList}} ->
                GraderProcess = spawn(fun() -> graderloop([]) end),
                NewState = maps:put(Name, {AssignmentList, true, [GraderProcess | GraderList]}, State),
                case valid(Submission, [], AssignmentList) of
                    false -> Pid ! {error, invalid_submission};
                    ValidSub -> 
                                try
                                    Feedback = eval(ValidSub, AssignmentList, GraderProcess),
                                    Pid ! {ok, Feedback}
                                catch
                                    _:_ -> totally_grader_failed
                                end
                end,
                {reply, finished, NewState}
        end.
    
    handle_cast({del_part, AssHandler, Label}, State) -> 
        {Name, _Ref} = AssHandler,
        case maps:find(Name, State) of
            {ok, {AssignmentList, Active, GraderList}} -> 
                case lists:keyfind(Label, 1, AssignmentList) of
                    false -> {noreply, State};
                    Tuple -> NewAssignmentList = lists:delete(Tuple, AssignmentList),
                             NewState = maps:put(Name, {NewAssignmentList, Active, GraderList}, State),
                             {noreply, NewState}
                end;
            _ -> {noreply, State}
        end.
        
    
    handle_info(_Info, State) ->
        {noreply, State}.
    
    terminate(_Reason, _State) ->
        ok.
    
    code_change(_OldVsn, State, _Extra) ->
        {ok, State}.
    
    % helper functions
    get_concurrent_grader([]) -> [];
    get_concurrent_grader([X|Xs]) -> 
        case X of
            {_Label, {troels, Gs}} -> 
                lists:append(Gs, get_concurrent_grader(Xs));
            _ -> get_concurrent_grader(Xs)
        end.
    
    get_module_grader([]) -> [];
    get_module_grader([X|Xs]) -> 
        case X of
            {_Label, {andrzej, Callback, Init}} -> 
                lists:append([{andrzej, Callback, Init}], get_module_grader(Xs));
            {andrzej, Callback, Init} ->
                lists:append([{andrzej, Callback, Init}], get_module_grader(Xs));
            _ -> get_module_grader(Xs)
        end.
    
    % combine get_concurrent_grader and get_module_grader
    get_mgrader(AL) ->
        CL = get_concurrent_grader(AL),
        AAL = lists:append(CL,AL),
        get_module_grader(AAL).
        
    
    % run all module grader
    run_all_module_grader([]) -> [];
    run_all_module_grader([X|Xs]) ->
        case X of
            {Label, {andrzej, Callback, Init}} -> 
                 case Callback:setup(Init) of
                            {ok, Salt} -> lists:append([{Label, {andrzej, Callback, Salt}}], run_all_module_grader(Xs));   
                            % throw?                        
                            Error -> Error
                 end;
            {Label, {troels, Gs}} ->
                try 
                    NGs = run_list_module_grader(Gs),
                    lists:append([{Label, {troels, NGs}}], run_all_module_grader(Xs))
                catch
                    _ -> grader_failed
                end;
            Other -> lists:append([Other],run_all_module_grader(Xs))
        end.
    run_list_module_grader([]) -> [];
    run_list_module_grader([X|Xs]) ->
        case X of
            {andrzej, Callback, Init} ->
                 case Callback:setup(Init) of
                            {ok, Salt} -> lists:append([{andrzej, Callback, Salt}], run_list_module_grader(Xs));   
                            % throw?                        
                            Error -> Error
                 end;
            Other -> lists:append([Other],run_list_module_grader(Xs))
        end.
    
    valid([],_,_) -> [];
    valid([{Label, Answer} | Xs], Cont, AssignmentList) ->
        case lists:keymember(Label, 1, AssignmentList) of
            true -> 
                case lists:member(Label, Cont) of
                    true -> valid(Xs, Cont, AssignmentList);
                    false -> lists:append([{Label, Answer}], valid(Xs, [Label|Cont], AssignmentList))
                end;
            false -> false
        end.
    
    % check submissions with label and Answers with label
    eval(_ValidSub,[],_) -> [];
    eval(ValidSub,[{Label,Grader} | AL],GraderProcess) ->
        case lists:keyfind(Label, 1, ValidSub) of
            false -> lists:append([{Label, missing}], eval(ValidSub, AL, GraderProcess));
            {Label,Answer} -> 
                try
                    Me = self(),
                    GraderProcess ! {Me,grading_start},
                    spawn(fun() -> 
                        try
                            Result = check(Answer, Grader),
                            Me ! Result
                        catch
                            _:_ -> grader_failed
                        end
                    end),
                    receive
                        Result -> 
                            GraderProcess ! {Me, grading_finish},
                            lists:append([{Label,Result}], eval(ValidSub,AL, GraderProcess))
                    after
                        3000 -> 
                            GraderProcess ! {Me, grading_finish},
                            lists:append([{Label,grader_failed}], eval(ValidSub,AL, GraderProcess))
                    end
                catch
                    _:_ -> lists:append([{Label,grader_failed}], eval(ValidSub,AL, GraderProcess))
                end
        end.
        
    % check Gs result or total grader fail
    troels_eval([],[]) -> [];
    troels_eval([A|As],[G|Gs]) ->
        Me = self(),    
        try 
            spawn(fun() -> 
                try
                    Res = check(A,G),
                    Me ! Res
                catch
                    _:_ -> grader_failed
                end
            end),
            Rs = troels_eval(As,Gs),
            receive
                Result -> lists:append(Rs, [Result])
            end
        catch
            _:_ -> throw(grader_failed)
        end;
    troels_eval(_,_) -> throw(grader_failed). 
    
    % check single Answer in Gs is correct or not
    check(A,G) ->
        case G of
            abraham -> looks_good;
            niels -> failed;
            {mikkel, Expect} -> 
                case (A =:= Expect) of
                    true -> looks_good;
                    _ -> failed
                end;
            {simon, Arg, Expect} ->
                case (A(Arg) =:= Expect) of
                    true -> looks_good;
                    false -> failed
                end;
            {andrzej, Callback, Salt} ->
                {ok, Result} = Callback:grade(Salt, {fakelabel, A}),
                Result;
            {troels, Gs} ->
                try
                    troels_eval(A, Gs)
                catch
                    _:_ -> grader_failed
                end;
            _ -> throw(grader_failed)
        end.
    
    unload_all([]) -> ok;
    unload_all([{andrzej, Callback, Salt}|Xs]) ->
        try
            Callback:unload(Salt),
            unload_all(Xs)
        catch
            Error -> Error
        end.
    
    graderloop(GraderList) ->
        receive
            {Pid, grading_start} -> 
                NewGraderList = [Pid | GraderList],
                graderloop(NewGraderList);
            {Pid, grading_finish} -> 
                graderloop(lists:delete(Pid, GraderList));
            {From, check_finished} ->
                case GraderList of
                    [] -> From ! all_finished;
                    _ -> graderloop(GraderList)
                end        
        end.
    check_no_grading([]) -> ok;
    check_no_grading([G|Gs]) ->
        From = self(),
        G ! {From, check_finished},
        receive
            all_finished -> check_no_grading(Gs)
        end.
    

      

  • 相关阅读:
    Linux新手随手笔记2.4-使用DHCP动态管理主机地址
    在word中输入任意角度旋转图片
    VBA注释临时
    VBA改写VBA代码
    【转载】robocopy的用法
    Excel数组排序+图片统一大小
    一键调节音量
    合并文档时让第一页为奇数
    智能跳转---TC资源管理器
    win7一键拖动生成快速启动栏
  • 原文地址:https://www.cnblogs.com/hanani/p/9981217.html
Copyright © 2020-2023  润新知